#include <sys/types.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/open.h>
#include <sys/cred.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/ksynch.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/debug.h>
#include <sys/cred.h>
#include <sys/promif.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cyclic.h>
#include <sys/machsystm.h>
#include <sys/vm.h>
#include <sys/cpu.h>
#include <sys/intreg.h>
#include <sys/machcpuvar.h>
#include <sys/mmu.h>
#include <sys/pte.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <vm/hat_sfmmu.h>
#include <sys/vm_machparam.h>
#include <vm/seg_kmem.h>
#include <vm/seg_kpm.h>
#include <sys/note.h>
#include <sys/ivintr.h>
#include <sys/hypervisor_api.h>
#include <sys/ldc.h>
#include <sys/ldc_impl.h>
#include <sys/cnex.h>
#include <sys/hsvc.h>
#include <sys/sdt.h>
#include <sys/kldc.h>
int i_ldc_h2v_error(int h_error);
void i_ldc_reset(ldc_chan_t *ldcp, boolean_t force_reset);
static int i_ldc_txq_reconf(ldc_chan_t *ldcp);
static int i_ldc_rxq_reconf(ldc_chan_t *ldcp, boolean_t force_reset);
static void i_ldc_rxq_drain(ldc_chan_t *ldcp);
static void i_ldc_reset_state(ldc_chan_t *ldcp);
static void i_ldc_debug_enter(void);
static int i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail);
static void i_ldc_get_tx_head(ldc_chan_t *ldcp, uint64_t *head);
static int i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail);
static int i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head);
static int i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype,
uint8_t ctrlmsg);
static int i_ldc_set_rxdq_head(ldc_chan_t *ldcp, uint64_t head);
static void i_ldc_rxdq_copy(ldc_chan_t *ldcp, uint64_t *head);
static uint64_t i_ldc_dq_rx_get_state(ldc_chan_t *ldcp, uint64_t *head,
uint64_t *tail, uint64_t *link_state);
static uint64_t i_ldc_hvq_rx_get_state(ldc_chan_t *ldcp, uint64_t *head,
uint64_t *tail, uint64_t *link_state);
static int i_ldc_rx_ackpeek(ldc_chan_t *ldcp, uint64_t rx_head,
uint64_t rx_tail);
static uint_t i_ldc_chkq(ldc_chan_t *ldcp);
static uint_t i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2);
static uint_t i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2);
static uint_t i_ldc_rx_process_hvq(ldc_chan_t *ldcp, boolean_t *notify_client,
uint64_t *notify_event);
static void i_ldc_clear_intr(ldc_chan_t *ldcp, cnex_intrtype_t itype);
static int i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep);
static int i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp,
size_t *sizep);
static int i_ldc_read_stream(ldc_chan_t *ldcp, caddr_t target_bufp,
size_t *sizep);
static int i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t target_bufp,
size_t *sizep);
static int i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t target_bufp,
size_t *sizep);
static int i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t target_bufp,
size_t *sizep);
static int i_ldc_check_seqid(ldc_chan_t *ldcp, ldc_msg_t *ldcmsg);
static int i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *ldcmsg);
static int i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg);
static int i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg);
static int i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg);
static int i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg);
static int i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg);
extern void i_ldc_mem_set_hsvc_vers(uint64_t major, uint64_t minor);
extern void i_ldc_init_mapin(ldc_soft_state_t *ldcssp, uint64_t major,
uint64_t minor);
static ldc_ver_t ldc_versions[] = { {1, 0} };
#define LDC_NUM_VERS (sizeof (ldc_versions) / sizeof (ldc_versions[0]))
#define ACKPEEK_HEAD_INVALID ((uint64_t)-1)
ldc_soft_state_t *ldcssp;
static struct modldrv md = {
&mod_miscops,
"sun4v LDC module",
};
static struct modlinkage ml = {
MODREV_1,
&md,
NULL
};
static uint64_t ldc_sup_minor;
static hsvc_info_t ldc_hsvc = {
HSVC_REV_1, NULL, HSVC_GROUP_LDC, 1, 2, "ldc"
};
uint64_t ldc_mtu_msgs = LDC_MTU_MSGS;
uint64_t ldc_queue_entries = LDC_QUEUE_ENTRIES;
uint64_t ldc_rxdq_multiplier = LDC_RXDQ_MULTIPLIER;
int ldc_max_retries = LDC_MAX_RETRIES;
clock_t ldc_delay = LDC_DELAY;
#define LDC_DEVCLASS_BIT(dc) (0x1 << (dc))
#define LDC_DEVCLASS_PROM_RESET(dc) \
(LDC_DEVCLASS_BIT(dc) & ldc_debug_reset_mask)
static uint64_t ldc_debug_reset_mask = LDC_DEVCLASS_BIT(LDC_DEV_BLK_SVC) |
LDC_DEVCLASS_BIT(LDC_DEV_GENERIC);
clock_t ldc_close_delay = LDC_CLOSE_DELAY;
uint64_t ldc_dring_direct_map_rsvd = LDC_DIRECT_MAP_SIZE_DEFAULT;
uint64_t ldc_direct_map_size_max = (16 * 1024 * 1024);
#ifdef DEBUG
int ldcdbg = 0x0;
int64_t ldcdbgchan = DBG_ALL_LDCS;
uint64_t ldc_inject_err_flag = 0;
void
ldcdebug(int64_t id, const char *fmt, ...)
{
char buf[512];
va_list ap;
if ((id != DBG_ALL_LDCS) &&
(ldcdbgchan != DBG_ALL_LDCS) &&
(ldcdbgchan != id)) {
return;
}
va_start(ap, fmt);
(void) vsprintf(buf, fmt, ap);
va_end(ap);
cmn_err(CE_CONT, "?%s", buf);
}
#define LDC_ERR_RESET 0x1
#define LDC_ERR_PKTLOSS 0x2
#define LDC_ERR_DQFULL 0x4
#define LDC_ERR_DRNGCLEAR 0x8
static boolean_t
ldc_inject_error(ldc_chan_t *ldcp, uint64_t error)
{
if ((ldcdbgchan != DBG_ALL_LDCS) && (ldcdbgchan != ldcp->id))
return (B_FALSE);
if ((ldc_inject_err_flag & error) == 0)
return (B_FALSE);
ldc_inject_err_flag &= ~error;
return (B_TRUE);
}
#define DUMP_PAYLOAD(id, addr) \
{ \
char buf[65*3]; \
int i; \
uint8_t *src = (uint8_t *)addr; \
for (i = 0; i < 64; i++, src++) \
(void) sprintf(&buf[i * 3], "|%02x", *src); \
(void) sprintf(&buf[i * 3], "|\n"); \
D2((id), "payload: %s", buf); \
}
#define DUMP_LDC_PKT(c, s, addr) \
{ \
ldc_msg_t *msg = (ldc_msg_t *)(addr); \
uint32_t mid = ((c)->mode != LDC_MODE_RAW) ? msg->seqid : 0; \
if (msg->type == LDC_DATA) { \
D2((c)->id, "%s: msg%d (/%x/%x/%x/,env[%c%c,sz=%d])", \
(s), mid, msg->type, msg->stype, msg->ctrl, \
(msg->env & LDC_FRAG_START) ? 'B' : ' ', \
(msg->env & LDC_FRAG_STOP) ? 'E' : ' ', \
(msg->env & LDC_LEN_MASK)); \
} else { \
D2((c)->id, "%s: msg%d (/%x/%x/%x/,env=%x)", (s), \
mid, msg->type, msg->stype, msg->ctrl, msg->env); \
} \
}
#define LDC_INJECT_RESET(_ldcp) ldc_inject_error(_ldcp, LDC_ERR_RESET)
#define LDC_INJECT_PKTLOSS(_ldcp) ldc_inject_error(_ldcp, LDC_ERR_PKTLOSS)
#define LDC_INJECT_DQFULL(_ldcp) ldc_inject_error(_ldcp, LDC_ERR_DQFULL)
#define LDC_INJECT_DRNGCLEAR(_ldcp) ldc_inject_error(_ldcp, LDC_ERR_DRNGCLEAR)
extern void i_ldc_mem_inject_dring_clear(ldc_chan_t *ldcp);
#else
#define DBG_ALL_LDCS -1
#define DUMP_PAYLOAD(id, addr)
#define DUMP_LDC_PKT(c, s, addr)
#define LDC_INJECT_RESET(_ldcp) (B_FALSE)
#define LDC_INJECT_PKTLOSS(_ldcp) (B_FALSE)
#define LDC_INJECT_DQFULL(_ldcp) (B_FALSE)
#define LDC_INJECT_DRNGCLEAR(_ldcp) (B_FALSE)
#endif
#define TRACE_RXDQ_LENGTH(ldcp) \
DTRACE_PROBE4(rxdq__size, \
uint64_t, ldcp->id, \
uint64_t, ldcp->rx_dq_head, \
uint64_t, ldcp->rx_dq_tail, \
uint64_t, ldcp->rx_dq_entries)
#define TRACE_RXHVQ_LENGTH(ldcp, head, tail) \
DTRACE_PROBE4(rxhvq__size, \
uint64_t, ldcp->id, \
uint64_t, head, \
uint64_t, tail, \
uint64_t, ldcp->rx_q_entries)
#define TRACE_RXDQ_COPY(ldcp, bytes) \
DTRACE_PROBE2(rxdq__copy, uint64_t, ldcp->id, uint64_t, bytes) \
#define Q_CONTIG_SPACE(head, tail, size) \
((head) <= (tail) ? ((size) - (tail)) : \
((head) - (tail) - LDC_PACKET_SIZE))
#define ZERO_PKT(p) \
bzero((p), sizeof (ldc_msg_t));
#define IDX2COOKIE(idx, pg_szc, pg_shift) \
(((pg_szc) << LDC_COOKIE_PGSZC_SHIFT) | ((idx) << (pg_shift)))
int
_init(void)
{
int status;
status = hsvc_register(&ldc_hsvc, &ldc_sup_minor);
if (status != 0) {
cmn_err(CE_NOTE, "!%s: cannot negotiate hypervisor LDC services"
" group: 0x%lx major: %ld minor: %ld errno: %d",
ldc_hsvc.hsvc_modname, ldc_hsvc.hsvc_group,
ldc_hsvc.hsvc_major, ldc_hsvc.hsvc_minor, status);
return (-1);
}
i_ldc_mem_set_hsvc_vers(ldc_hsvc.hsvc_major, ldc_sup_minor);
ldcssp = kmem_zalloc(sizeof (ldc_soft_state_t), KM_SLEEP);
i_ldc_init_mapin(ldcssp, ldc_hsvc.hsvc_major, ldc_sup_minor);
status = mod_install(&ml);
if (status != 0) {
kmem_free(ldcssp, sizeof (ldc_soft_state_t));
return (status);
}
mutex_init(&ldcssp->lock, NULL, MUTEX_DRIVER, NULL);
mutex_enter(&ldcssp->lock);
ldcssp->memhdl_cache = kmem_cache_create("ldc_memhdl_cache",
sizeof (ldc_mhdl_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
if (ldcssp->memhdl_cache == NULL) {
DWARN(DBG_ALL_LDCS, "_init: ldc_memhdl cache create failed\n");
mutex_exit(&ldcssp->lock);
return (-1);
}
ldcssp->memseg_cache = kmem_cache_create("ldc_memseg_cache",
sizeof (ldc_memseg_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
if (ldcssp->memseg_cache == NULL) {
DWARN(DBG_ALL_LDCS, "_init: ldc_memseg cache create failed\n");
mutex_exit(&ldcssp->lock);
return (-1);
}
ldcssp->channel_count = 0;
ldcssp->channels_open = 0;
ldcssp->chan_list = NULL;
ldcssp->dring_list = NULL;
kldc_set_debug_cb(&i_ldc_debug_enter);
mutex_exit(&ldcssp->lock);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&ml, modinfop));
}
int
_fini(void)
{
int rv, status;
ldc_chan_t *tmp_ldcp, *ldcp;
ldc_dring_t *tmp_dringp, *dringp;
ldc_mem_info_t minfo;
status = mod_remove(&ml);
if (status) {
DWARN(DBG_ALL_LDCS, "_fini: mod_remove failed\n");
return (EIO);
}
kldc_set_debug_cb(NULL);
dringp = ldcssp->dring_list;
while (dringp != NULL) {
tmp_dringp = dringp->next;
rv = ldc_mem_dring_info((ldc_dring_handle_t)dringp, &minfo);
if (rv == 0 && minfo.status != LDC_UNBOUND) {
if (minfo.status == LDC_BOUND) {
(void) ldc_mem_dring_unbind(
(ldc_dring_handle_t)dringp);
}
if (minfo.status == LDC_MAPPED) {
(void) ldc_mem_dring_unmap(
(ldc_dring_handle_t)dringp);
}
}
(void) ldc_mem_dring_destroy((ldc_dring_handle_t)dringp);
dringp = tmp_dringp;
}
ldcssp->dring_list = NULL;
ldcp = ldcssp->chan_list;
while (ldcp != NULL) {
tmp_ldcp = ldcp->next;
(void) ldc_close((ldc_handle_t)ldcp);
(void) ldc_fini((ldc_handle_t)ldcp);
ldcp = tmp_ldcp;
}
ldcssp->chan_list = NULL;
kmem_cache_destroy(ldcssp->memhdl_cache);
kmem_cache_destroy(ldcssp->memseg_cache);
mutex_destroy(&ldcssp->lock);
kmem_free(ldcssp, sizeof (ldc_soft_state_t));
(void) hsvc_unregister(&ldc_hsvc);
return (status);
}
int
i_ldc_h2v_error(int h_error)
{
switch (h_error) {
case H_EOK:
return (0);
case H_ENORADDR:
return (EFAULT);
case H_EBADPGSZ:
case H_EINVAL:
return (EINVAL);
case H_EWOULDBLOCK:
return (EWOULDBLOCK);
case H_ENOACCESS:
case H_ENOMAP:
return (EACCES);
case H_EIO:
case H_ECPUERROR:
return (EIO);
case H_ENOTSUPPORTED:
return (ENOTSUP);
case H_ETOOMANY:
return (ENOSPC);
case H_ECHANNEL:
return (ECHRNG);
default:
break;
}
return (EIO);
}
static int
i_ldc_txq_reconf(ldc_chan_t *ldcp)
{
int rv;
ASSERT(MUTEX_HELD(&ldcp->lock));
ASSERT(MUTEX_HELD(&ldcp->tx_lock));
rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries);
if (rv) {
cmn_err(CE_WARN,
"i_ldc_txq_reconf: (0x%lx) cannot set qconf", ldcp->id);
return (EIO);
}
rv = hv_ldc_tx_get_state(ldcp->id, &(ldcp->tx_head),
&(ldcp->tx_tail), &(ldcp->link_state));
if (rv) {
cmn_err(CE_WARN,
"i_ldc_txq_reconf: (0x%lx) cannot get qptrs", ldcp->id);
return (EIO);
}
D1(ldcp->id, "i_ldc_txq_reconf: (0x%llx) h=0x%llx,t=0x%llx,"
"s=0x%llx\n", ldcp->id, ldcp->tx_head, ldcp->tx_tail,
ldcp->link_state);
return (0);
}
static int
i_ldc_rxq_reconf(ldc_chan_t *ldcp, boolean_t force_reset)
{
int rv;
uint64_t rx_head, rx_tail;
ASSERT(MUTEX_HELD(&ldcp->lock));
rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
&(ldcp->link_state));
if (rv) {
cmn_err(CE_WARN,
"i_ldc_rxq_reconf: (0x%lx) cannot get state",
ldcp->id);
return (EIO);
}
if (force_reset || (ldcp->tstate & ~TS_IN_RESET) == TS_UP) {
rv = hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra,
ldcp->rx_q_entries);
if (rv) {
cmn_err(CE_WARN,
"i_ldc_rxq_reconf: (0x%lx) cannot set qconf",
ldcp->id);
return (EIO);
}
D1(ldcp->id, "i_ldc_rxq_reconf: (0x%llx) completed q reconf",
ldcp->id);
}
return (0);
}
static void
i_ldc_rxq_drain(ldc_chan_t *ldcp)
{
int rv;
uint64_t rx_head, rx_tail;
int retries = 0;
ASSERT(MUTEX_HELD(&ldcp->lock));
rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
&(ldcp->link_state));
if (rv) {
cmn_err(CE_WARN, "i_ldc_rxq_drain: (0x%lx) cannot get state, "
"rv = 0x%x", ldcp->id, rv);
return;
}
if (rx_head == rx_tail)
return;
if ((rv = hv_ldc_rx_set_qhead(ldcp->id, rx_tail)) == 0)
return;
while ((rv == H_EWOULDBLOCK) && (retries++ < ldc_max_retries)) {
drv_usecwait(ldc_delay);
if ((rv = hv_ldc_rx_set_qhead(ldcp->id, rx_tail)) == 0)
return;
}
cmn_err(CE_WARN, "i_ldc_rxq_drain: (0x%lx) cannot set qhead 0x%lx, "
"rv = 0x%x", ldcp->id, rx_tail, rv);
}
static void
i_ldc_reset_state(ldc_chan_t *ldcp)
{
ASSERT(MUTEX_HELD(&ldcp->lock));
ldcp->last_msg_snt = LDC_INIT_SEQID;
ldcp->last_ack_rcd = 0;
ldcp->last_msg_rcd = 0;
ldcp->tx_ackd_head = ldcp->tx_head;
ldcp->stream_remains = 0;
ldcp->next_vidx = 0;
ldcp->hstate = 0;
ldcp->tstate = TS_OPEN;
ldcp->status = LDC_OPEN;
ldcp->rx_ack_head = ACKPEEK_HEAD_INVALID;
ldcp->rx_dq_head = 0;
ldcp->rx_dq_tail = 0;
if (ldcp->link_state == LDC_CHANNEL_UP ||
ldcp->link_state == LDC_CHANNEL_RESET) {
if (ldcp->mode == LDC_MODE_RAW) {
ldcp->status = LDC_UP;
ldcp->tstate = TS_UP;
} else {
ldcp->status = LDC_READY;
ldcp->tstate |= TS_LINK_READY;
}
}
}
void
i_ldc_reset(ldc_chan_t *ldcp, boolean_t force_reset)
{
DWARN(ldcp->id, "i_ldc_reset: (0x%llx) channel reset\n", ldcp->id);
ASSERT(MUTEX_HELD(&ldcp->lock));
ASSERT(MUTEX_HELD(&ldcp->tx_lock));
(void) i_ldc_txq_reconf(ldcp);
(void) i_ldc_rxq_reconf(ldcp, force_reset);
(void) i_ldc_clear_intr(ldcp, CNEX_TX_INTR);
(void) i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
i_ldc_reset_state(ldcp);
ldcp->tstate |= TS_IN_RESET;
}
static void
i_ldc_debug_enter(void)
{
ldc_chan_t *ldcp;
ldcp = ldcssp->chan_list;
while (ldcp != NULL) {
if (((ldcp->tstate & TS_QCONF_RDY) == TS_QCONF_RDY) &&
(LDC_DEVCLASS_PROM_RESET(ldcp->devclass) != 0)) {
(void) hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra,
ldcp->rx_q_entries);
}
ldcp = ldcp->next;
}
}
static void
i_ldc_clear_intr(ldc_chan_t *ldcp, cnex_intrtype_t itype)
{
ldc_cnex_t *cinfo = &ldcssp->cinfo;
ASSERT(MUTEX_HELD(&ldcp->lock));
ASSERT(cinfo->dip != NULL);
switch (itype) {
case CNEX_TX_INTR:
if (ldcp->tx_intr_state)
ldcp->tx_intr_state = LDC_INTR_NONE;
else
return;
break;
case CNEX_RX_INTR:
if (ldcp->rx_intr_state)
ldcp->rx_intr_state = LDC_INTR_NONE;
else
return;
break;
}
(void) cinfo->clr_intr(cinfo->dip, ldcp->id, itype);
D2(ldcp->id,
"i_ldc_clear_intr: (0x%llx) cleared 0x%x intr\n",
ldcp->id, itype);
}
static int
i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head)
{
int rv;
int retries;
ASSERT(MUTEX_HELD(&ldcp->lock));
for (retries = 0; retries < ldc_max_retries; retries++) {
if ((rv = hv_ldc_rx_set_qhead(ldcp->id, head)) == 0)
return (0);
if (rv != H_EWOULDBLOCK)
break;
drv_usecwait(ldc_delay);
}
cmn_err(CE_WARN, "ldc_set_rx_qhead: (0x%lx) cannot set qhead 0x%lx, "
"rv = 0x%x", ldcp->id, head, rv);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
static void
i_ldc_get_tx_head(ldc_chan_t *ldcp, uint64_t *head)
{
ldc_msg_t *pkt;
ASSERT(MUTEX_HELD(&ldcp->tx_lock));
*head = ldcp->tx_head;
if (ldcp->mode == LDC_MODE_RELIABLE) {
while (ldcp->tx_ackd_head != ldcp->tx_head) {
pkt = (ldc_msg_t *)(ldcp->tx_q_va + ldcp->tx_ackd_head);
if ((pkt->type & LDC_DATA) && (pkt->stype & LDC_INFO)) {
break;
}
ldcp->tx_ackd_head =
(ldcp->tx_ackd_head + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
}
*head = ldcp->tx_ackd_head;
}
}
static int
i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail)
{
int rv;
uint64_t current_head, new_tail;
ASSERT(MUTEX_HELD(&ldcp->tx_lock));
rv = hv_ldc_tx_get_state(ldcp->id,
&ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
if (rv) {
cmn_err(CE_WARN,
"i_ldc_get_tx_tail: (0x%lx) cannot read qptrs\n",
ldcp->id);
return (EIO);
}
if (ldcp->link_state == LDC_CHANNEL_DOWN) {
D1(ldcp->id, "i_ldc_get_tx_tail: (0x%llx) channel not ready\n",
ldcp->id);
return (ECONNRESET);
}
i_ldc_get_tx_head(ldcp, ¤t_head);
new_tail = (ldcp->tx_tail + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
if (new_tail == current_head) {
DWARN(ldcp->id,
"i_ldc_get_tx_tail: (0x%llx) TX queue is full\n",
ldcp->id);
return (EWOULDBLOCK);
}
D2(ldcp->id, "i_ldc_get_tx_tail: (0x%llx) head=0x%llx, tail=0x%llx\n",
ldcp->id, ldcp->tx_head, ldcp->tx_tail);
*tail = ldcp->tx_tail;
return (0);
}
static int
i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail)
{
int rv, retval = EWOULDBLOCK;
int retries;
ASSERT(MUTEX_HELD(&ldcp->tx_lock));
for (retries = 0; retries < ldc_max_retries; retries++) {
if ((rv = hv_ldc_tx_set_qtail(ldcp->id, tail)) == 0) {
retval = 0;
break;
}
if (rv != H_EWOULDBLOCK) {
DWARN(ldcp->id, "i_ldc_set_tx_tail: (0x%llx) set "
"qtail=0x%llx failed, rv=%d\n", ldcp->id, tail, rv);
retval = EIO;
break;
}
drv_usecwait(ldc_delay);
}
return (retval);
}
void
i_ldc_rxdq_copy(ldc_chan_t *ldcp, uint64_t *head)
{
uint64_t q_size, dq_size;
ASSERT(MUTEX_HELD(&ldcp->lock));
q_size = ldcp->rx_q_entries << LDC_PACKET_SHIFT;
dq_size = ldcp->rx_dq_entries << LDC_PACKET_SHIFT;
ASSERT(Q_CONTIG_SPACE(ldcp->rx_dq_head, ldcp->rx_dq_tail,
dq_size) >= LDC_PACKET_SIZE);
bcopy((void *)(ldcp->rx_q_va + *head),
(void *)(ldcp->rx_dq_va + ldcp->rx_dq_tail), LDC_PACKET_SIZE);
TRACE_RXDQ_COPY(ldcp, LDC_PACKET_SIZE);
*head = (*head + LDC_PACKET_SIZE) % q_size;
ldcp->rx_dq_tail = (ldcp->rx_dq_tail + LDC_PACKET_SIZE) % dq_size;
}
static int
i_ldc_set_rxdq_head(ldc_chan_t *ldcp, uint64_t head)
{
ldcp->rx_dq_head = head;
return (0);
}
static uint64_t
i_ldc_dq_rx_get_state(ldc_chan_t *ldcp, uint64_t *head, uint64_t *tail,
uint64_t *link_state)
{
_NOTE(ARGUNUSED(link_state))
*head = ldcp->rx_dq_head;
*tail = ldcp->rx_dq_tail;
return (0);
}
static uint64_t
i_ldc_hvq_rx_get_state(ldc_chan_t *ldcp, uint64_t *head, uint64_t *tail,
uint64_t *link_state)
{
return (i_ldc_h2v_error(hv_ldc_rx_get_state(ldcp->id, head, tail,
link_state)));
}
static uint_t
i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2)
{
_NOTE(ARGUNUSED(arg2))
ldc_chan_t *ldcp;
boolean_t notify;
uint64_t event;
int rv, status;
if (arg1 == NULL) {
cmn_err(CE_WARN, "i_ldc_rx_hdlr: invalid arg\n");
return (DDI_INTR_UNCLAIMED);
}
ldcp = (ldc_chan_t *)arg1;
D1(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) Received intr, ldcp=0x%p\n",
ldcp->id, ldcp);
D1(ldcp->id, "i_ldc_rx_hdlr: (%llx) USR%lx/TS%lx/HS%lx, LSTATE=%lx\n",
ldcp->id, ldcp->status, ldcp->tstate, ldcp->hstate,
ldcp->link_state);
mutex_enter(&ldcp->lock);
ldcp->rx_intr_state = LDC_INTR_ACTIVE;
status = i_ldc_rx_process_hvq(ldcp, ¬ify, &event);
if (ldcp->mode != LDC_MODE_RELIABLE) {
if ((event & LDC_EVT_READ) == 0) {
i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
} else {
ldcp->rx_intr_state = LDC_INTR_PEND;
}
}
if (notify && ldcp->cb_enabled) {
ldcp->cb_inprogress = B_TRUE;
mutex_exit(&ldcp->lock);
rv = ldcp->cb(event, ldcp->cb_arg);
if (rv) {
DWARN(ldcp->id,
"i_ldc_rx_hdlr: (0x%llx) callback failure",
ldcp->id);
}
mutex_enter(&ldcp->lock);
ldcp->cb_inprogress = B_FALSE;
}
if (ldcp->mode == LDC_MODE_RELIABLE) {
if (status == ENOSPC) {
ldcp->rx_intr_state = LDC_INTR_PEND;
} else {
i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
}
}
mutex_exit(&ldcp->lock);
D1(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) exiting handler", ldcp->id);
return (DDI_INTR_CLAIMED);
}
static uint_t
i_ldc_chkq(ldc_chan_t *ldcp)
{
boolean_t notify;
uint64_t event;
return (i_ldc_rx_process_hvq(ldcp, ¬ify, &event));
}
static int
i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype,
uint8_t ctrlmsg)
{
int rv;
ldc_msg_t *pkt;
uint64_t tx_tail;
uint32_t curr_seqid;
mutex_enter(&ldcp->tx_lock);
curr_seqid = ldcp->last_msg_snt;
rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
if (rv) {
DWARN(ldcp->id,
"i_ldc_send_pkt: (0x%llx) error sending pkt, "
"type=0x%x,subtype=0x%x,ctrl=0x%x\n",
ldcp->id, pkttype, subtype, ctrlmsg);
mutex_exit(&ldcp->tx_lock);
return (rv);
}
pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
ZERO_PKT(pkt);
pkt->type = pkttype;
pkt->stype = subtype;
pkt->ctrl = ctrlmsg;
if (((ctrlmsg & LDC_CTRL_MASK) != LDC_RTS) &&
((ctrlmsg & LDC_CTRL_MASK) != LDC_RTR)) {
curr_seqid++;
if (ldcp->mode != LDC_MODE_RAW) {
pkt->seqid = curr_seqid;
pkt->ackid = ldcp->last_msg_rcd;
}
}
DUMP_LDC_PKT(ldcp, "i_ldc_send_pkt", (uint64_t)pkt);
tx_tail = (tx_tail + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
rv = i_ldc_set_tx_tail(ldcp, tx_tail);
if (rv) {
DWARN(ldcp->id,
"i_ldc_send_pkt:(0x%llx) error sending pkt, "
"type=0x%x,stype=0x%x,ctrl=0x%x\n",
ldcp->id, pkttype, subtype, ctrlmsg);
mutex_exit(&ldcp->tx_lock);
return (EIO);
}
ldcp->last_msg_snt = curr_seqid;
ldcp->tx_tail = tx_tail;
mutex_exit(&ldcp->tx_lock);
return (0);
}
static int
i_ldc_check_seqid(ldc_chan_t *ldcp, ldc_msg_t *msg)
{
if (ldcp->mode == LDC_MODE_RAW)
return (0);
if (msg->ctrl == LDC_VER ||
msg->ctrl == LDC_RTS ||
msg->ctrl == LDC_RTR)
return (0);
if (msg->seqid != (ldcp->last_msg_rcd + 1)) {
DWARN(ldcp->id,
"i_ldc_check_seqid: (0x%llx) out-of-order pkt, got 0x%x, "
"expecting 0x%x\n", ldcp->id, msg->seqid,
(ldcp->last_msg_rcd + 1));
return (EIO);
}
#ifdef DEBUG
if (LDC_INJECT_PKTLOSS(ldcp)) {
DWARN(ldcp->id,
"i_ldc_check_seqid: (0x%llx) inject pkt loss\n", ldcp->id);
return (EIO);
}
#endif
return (0);
}
static int
i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg)
{
int rv = 0, idx = ldcp->next_vidx;
ldc_msg_t *pkt;
uint64_t tx_tail;
ldc_ver_t *rcvd_ver;
rcvd_ver = (ldc_ver_t *)((uint64_t)msg + LDC_PAYLOAD_VER_OFF);
D2(ldcp->id, "i_ldc_process_VER: (0x%llx) received VER v%u.%u\n",
ldcp->id, rcvd_ver->major, rcvd_ver->minor);
mutex_enter(&ldcp->tx_lock);
switch (msg->stype) {
case LDC_INFO:
if ((ldcp->tstate & ~TS_IN_RESET) == TS_VREADY) {
(void) i_ldc_txq_reconf(ldcp);
i_ldc_reset_state(ldcp);
mutex_exit(&ldcp->tx_lock);
return (EAGAIN);
}
rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
if (rv != 0) {
DWARN(ldcp->id,
"i_ldc_process_VER: (0x%llx) err sending "
"version ACK/NACK\n", ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
ZERO_PKT(pkt);
pkt->type = LDC_CTRL;
pkt->ctrl = LDC_VER;
for (;;) {
D1(ldcp->id, "i_ldc_process_VER: got %u.%u chk %u.%u\n",
rcvd_ver->major, rcvd_ver->minor,
ldc_versions[idx].major, ldc_versions[idx].minor);
if (rcvd_ver->major == ldc_versions[idx].major) {
pkt->stype = LDC_ACK;
if (rcvd_ver->minor > ldc_versions[idx].minor)
rcvd_ver->minor =
ldc_versions[idx].minor;
bcopy(rcvd_ver, pkt->udata, sizeof (*rcvd_ver));
break;
}
if (rcvd_ver->major > ldc_versions[idx].major) {
D1(ldcp->id, "i_ldc_process_VER: using next"
" lower idx=%d, v%u.%u\n", idx,
ldc_versions[idx].major,
ldc_versions[idx].minor);
pkt->stype = LDC_NACK;
bcopy(&ldc_versions[idx], pkt->udata,
sizeof (ldc_versions[idx]));
ldcp->next_vidx = idx;
break;
}
idx++;
D1(ldcp->id, "i_ldc_process_VER: inc idx %x\n", idx);
if (idx == LDC_NUM_VERS) {
pkt->stype = LDC_NACK;
bzero(pkt->udata, sizeof (ldc_ver_t));
ldcp->next_vidx = 0;
break;
}
}
tx_tail = (tx_tail + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
rv = i_ldc_set_tx_tail(ldcp, tx_tail);
if (rv == 0) {
ldcp->tx_tail = tx_tail;
if (pkt->stype == LDC_ACK) {
D2(ldcp->id, "i_ldc_process_VER: (0x%llx) sent"
" version ACK\n", ldcp->id);
ldcp->version.major = rcvd_ver->major;
ldcp->version.minor = rcvd_ver->minor;
ldcp->hstate |= TS_RCVD_VER;
ldcp->tstate |= TS_VER_DONE;
D1(DBG_ALL_LDCS,
"(0x%llx) Sent ACK, "
"Agreed on version v%u.%u\n",
ldcp->id, rcvd_ver->major, rcvd_ver->minor);
}
} else {
DWARN(ldcp->id,
"i_ldc_process_VER: (0x%llx) error sending "
"ACK/NACK\n", ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
break;
case LDC_ACK:
if ((ldcp->tstate & ~TS_IN_RESET) == TS_VREADY) {
if (ldcp->version.major != rcvd_ver->major ||
ldcp->version.minor != rcvd_ver->minor) {
DWARN(ldcp->id,
"i_ldc_process_VER: (0x%llx) recvd"
" ACK ver != sent ACK ver\n", ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
} else {
ldcp->version.major = rcvd_ver->major;
ldcp->version.minor = rcvd_ver->minor;
ldcp->tstate |= TS_VER_DONE;
}
D1(ldcp->id, "(0x%llx) Got ACK, Agreed on version v%u.%u\n",
ldcp->id, rcvd_ver->major, rcvd_ver->minor);
rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
if (rv) {
DWARN(ldcp->id,
"i_ldc_process_VER: (0x%llx) cannot send RTS\n",
ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
ZERO_PKT(pkt);
pkt->type = LDC_CTRL;
pkt->stype = LDC_INFO;
pkt->ctrl = LDC_RTS;
pkt->env = ldcp->mode;
if (ldcp->mode != LDC_MODE_RAW)
pkt->seqid = LDC_INIT_SEQID;
ldcp->last_msg_rcd = LDC_INIT_SEQID;
DUMP_LDC_PKT(ldcp, "i_ldc_process_VER snd rts", (uint64_t)pkt);
tx_tail = (tx_tail + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
rv = i_ldc_set_tx_tail(ldcp, tx_tail);
if (rv) {
D2(ldcp->id,
"i_ldc_process_VER: (0x%llx) no listener\n",
ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
ldcp->tx_tail = tx_tail;
ldcp->hstate |= TS_SENT_RTS;
break;
case LDC_NACK:
if (rcvd_ver->major == 0 && rcvd_ver->minor == 0) {
DWARN(DBG_ALL_LDCS,
"i_ldc_process_VER: (0x%llx) no version match\n",
ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
if (rv != 0) {
cmn_err(CE_NOTE,
"i_ldc_process_VER: (0x%lx) err sending "
"version ACK/NACK\n", ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
ZERO_PKT(pkt);
pkt->type = LDC_CTRL;
pkt->ctrl = LDC_VER;
pkt->stype = LDC_INFO;
for (;;) {
if (rcvd_ver->major == ldc_versions[idx].major) {
if (rcvd_ver->minor > ldc_versions[idx].minor)
rcvd_ver->minor =
ldc_versions[idx].minor;
bcopy(rcvd_ver, pkt->udata, sizeof (*rcvd_ver));
break;
}
if (rcvd_ver->major > ldc_versions[idx].major) {
D1(ldcp->id, "i_ldc_process_VER: using next"
" lower idx=%d, v%u.%u\n", idx,
ldc_versions[idx].major,
ldc_versions[idx].minor);
bcopy(&ldc_versions[idx], pkt->udata,
sizeof (ldc_versions[idx]));
ldcp->next_vidx = idx;
break;
}
idx++;
D1(ldcp->id, "i_ldc_process_VER: inc idx %x\n", idx);
if (idx == LDC_NUM_VERS) {
ldcp->next_vidx = 0;
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
}
tx_tail = (tx_tail + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
rv = i_ldc_set_tx_tail(ldcp, tx_tail);
if (rv == 0) {
D2(ldcp->id, "i_ldc_process_VER: (0x%llx) sent version"
"INFO v%u.%u\n", ldcp->id, ldc_versions[idx].major,
ldc_versions[idx].minor);
ldcp->tx_tail = tx_tail;
} else {
cmn_err(CE_NOTE,
"i_ldc_process_VER: (0x%lx) error sending version"
"INFO\n", ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
break;
}
mutex_exit(&ldcp->tx_lock);
return (rv);
}
static int
i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg)
{
int rv = 0;
ldc_msg_t *pkt;
uint64_t tx_tail;
boolean_t sent_NACK = B_FALSE;
D2(ldcp->id, "i_ldc_process_RTS: (0x%llx) received RTS\n", ldcp->id);
switch (msg->stype) {
case LDC_NACK:
DWARN(ldcp->id,
"i_ldc_process_RTS: (0x%llx) RTS NACK received\n",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
case LDC_INFO:
if (ldcp->mode != (ldc_mode_t)msg->env) {
cmn_err(CE_NOTE,
"i_ldc_process_RTS: (0x%lx) mode mismatch\n",
ldcp->id);
rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTS);
if (rv) {
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
}
sent_NACK = B_TRUE;
}
break;
default:
DWARN(ldcp->id, "i_ldc_process_RTS: (0x%llx) unexp ACK\n",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
}
if (rv || sent_NACK)
return (rv);
ldcp->hstate |= TS_RCVD_RTS;
ldcp->last_msg_snt = msg->seqid;
mutex_enter(&ldcp->tx_lock);
rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
if (rv != 0) {
cmn_err(CE_NOTE,
"i_ldc_process_RTS: (0x%lx) err sending RTR\n",
ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
ZERO_PKT(pkt);
pkt->type = LDC_CTRL;
pkt->stype = LDC_INFO;
pkt->ctrl = LDC_RTR;
pkt->env = ldcp->mode;
if (ldcp->mode != LDC_MODE_RAW)
pkt->seqid = LDC_INIT_SEQID;
ldcp->last_msg_rcd = msg->seqid;
tx_tail = (tx_tail + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
rv = i_ldc_set_tx_tail(ldcp, tx_tail);
if (rv == 0) {
D2(ldcp->id,
"i_ldc_process_RTS: (0x%llx) sent RTR\n", ldcp->id);
DUMP_LDC_PKT(ldcp, "i_ldc_process_RTS sent rtr", (uint64_t)pkt);
ldcp->tx_tail = tx_tail;
ldcp->hstate |= TS_SENT_RTR;
} else {
cmn_err(CE_NOTE,
"i_ldc_process_RTS: (0x%lx) error sending RTR\n",
ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
mutex_exit(&ldcp->tx_lock);
return (0);
}
static int
i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg)
{
int rv = 0;
boolean_t sent_NACK = B_FALSE;
D2(ldcp->id, "i_ldc_process_RTR: (0x%llx) received RTR\n", ldcp->id);
switch (msg->stype) {
case LDC_NACK:
DWARN(ldcp->id,
"i_ldc_process_RTR: (0x%llx) RTR NACK received\n",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
case LDC_INFO:
if (ldcp->mode != (ldc_mode_t)msg->env) {
DWARN(ldcp->id,
"i_ldc_process_RTR: (0x%llx) mode mismatch, "
"expecting 0x%x, got 0x%x\n",
ldcp->id, ldcp->mode, (ldc_mode_t)msg->env);
rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTR);
if (rv) {
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
}
sent_NACK = B_TRUE;
}
break;
default:
DWARN(ldcp->id, "i_ldc_process_RTR: (0x%llx) unexp ACK\n",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
}
if (rv || sent_NACK)
return (rv);
ldcp->last_msg_snt = msg->seqid;
ldcp->hstate |= TS_RCVD_RTR;
rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_INFO, LDC_RDX);
if (rv) {
cmn_err(CE_NOTE,
"i_ldc_process_RTR: (0x%lx) cannot send RDX\n",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
D2(ldcp->id,
"i_ldc_process_RTR: (0x%llx) sent RDX\n", ldcp->id);
ldcp->hstate |= TS_SENT_RDX;
ldcp->tstate |= TS_HSHAKE_DONE;
if ((ldcp->tstate & TS_IN_RESET) == 0)
ldcp->status = LDC_UP;
D1(ldcp->id, "(0x%llx) Handshake Complete\n", ldcp->id);
return (0);
}
static int
i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg)
{
int rv = 0;
D2(ldcp->id, "i_ldc_process_RDX: (0x%llx) received RDX\n", ldcp->id);
switch (msg->stype) {
case LDC_NACK:
DWARN(ldcp->id,
"i_ldc_process_RDX: (0x%llx) RDX NACK received\n",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
case LDC_INFO:
if ((ldcp->tstate == TS_UP) && (ldcp->hstate & TS_RCVD_RDX)) {
DWARN(DBG_ALL_LDCS,
"i_ldc_process_RDX: (0x%llx) unexpected RDX"
" - LDC reset\n", ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
ldcp->hstate |= TS_RCVD_RDX;
ldcp->tstate |= TS_HSHAKE_DONE;
if ((ldcp->tstate & TS_IN_RESET) == 0)
ldcp->status = LDC_UP;
D1(DBG_ALL_LDCS, "(0x%llx) Handshake Complete\n", ldcp->id);
break;
default:
DWARN(ldcp->id, "i_ldc_process_RDX: (0x%llx) unexp ACK\n",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
}
return (rv);
}
static int
i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg)
{
int rv;
uint64_t tx_head;
ldc_msg_t *pkt;
mutex_enter(&ldcp->tx_lock);
rv = hv_ldc_tx_get_state(ldcp->id,
&ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
if (rv != 0) {
cmn_err(CE_WARN,
"i_ldc_process_data_ACK: (0x%lx) cannot read qptrs\n",
ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
tx_head = ldcp->tx_ackd_head;
for (;;) {
pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_head);
tx_head = (tx_head + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
if (pkt->seqid == msg->ackid) {
D2(ldcp->id,
"i_ldc_process_data_ACK: (0x%llx) found packet\n",
ldcp->id);
ldcp->last_ack_rcd = msg->ackid;
ldcp->tx_ackd_head = tx_head;
break;
}
if (tx_head == ldcp->tx_head) {
DWARN(ldcp->id,
"i_ldc_process_data_ACK: (0x%llx) invalid ACKid\n",
ldcp->id);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
}
mutex_exit(&ldcp->tx_lock);
return (0);
}
static int
i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *msg)
{
int rv = 0;
D1(ldcp->id, "i_ldc_ctrlmsg: (%llx) tstate = %lx, hstate = %lx\n",
ldcp->id, ldcp->tstate, ldcp->hstate);
switch (ldcp->tstate & ~TS_IN_RESET) {
case TS_OPEN:
case TS_READY:
switch (msg->ctrl & LDC_CTRL_MASK) {
case LDC_VER:
rv = i_ldc_process_VER(ldcp, msg);
break;
default:
DWARN(ldcp->id,
"i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
"tstate=0x%x\n", ldcp->id,
(msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
break;
}
break;
case TS_VREADY:
switch (msg->ctrl & LDC_CTRL_MASK) {
case LDC_VER:
rv = i_ldc_process_VER(ldcp, msg);
break;
case LDC_RTS:
rv = i_ldc_process_RTS(ldcp, msg);
break;
case LDC_RTR:
rv = i_ldc_process_RTR(ldcp, msg);
break;
case LDC_RDX:
rv = i_ldc_process_RDX(ldcp, msg);
break;
default:
DWARN(ldcp->id,
"i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
"tstate=0x%x\n", ldcp->id,
(msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
break;
}
break;
case TS_UP:
switch (msg->ctrl & LDC_CTRL_MASK) {
case LDC_VER:
DWARN(ldcp->id,
"i_ldc_ctrlmsg: (0x%llx) unexpected VER "
"- LDC reset\n", ldcp->id);
mutex_enter(&ldcp->tx_lock);
(void) i_ldc_txq_reconf(ldcp);
i_ldc_reset_state(ldcp);
mutex_exit(&ldcp->tx_lock);
rv = EAGAIN;
break;
case LDC_RDX:
rv = i_ldc_process_RDX(ldcp, msg);
break;
default:
DWARN(ldcp->id,
"i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
"tstate=0x%x\n", ldcp->id,
(msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
break;
}
}
return (rv);
}
static int
i_ldc_register_channel(ldc_chan_t *ldcp)
{
int rv = 0;
ldc_cnex_t *cinfo = &ldcssp->cinfo;
if (cinfo->dip == NULL) {
DWARN(ldcp->id,
"i_ldc_register_channel: cnex has not registered\n");
return (EAGAIN);
}
rv = cinfo->reg_chan(cinfo->dip, ldcp->id, ldcp->devclass);
if (rv) {
DWARN(ldcp->id,
"i_ldc_register_channel: cannot register channel\n");
return (rv);
}
rv = cinfo->add_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR,
i_ldc_tx_hdlr, ldcp, NULL);
if (rv) {
DWARN(ldcp->id,
"i_ldc_register_channel: cannot add Tx interrupt\n");
(void) cinfo->unreg_chan(cinfo->dip, ldcp->id);
return (rv);
}
rv = cinfo->add_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR,
i_ldc_rx_hdlr, ldcp, NULL);
if (rv) {
DWARN(ldcp->id,
"i_ldc_register_channel: cannot add Rx interrupt\n");
(void) cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR);
(void) cinfo->unreg_chan(cinfo->dip, ldcp->id);
return (rv);
}
ldcp->tstate |= TS_CNEX_RDY;
return (0);
}
static int
i_ldc_unregister_channel(ldc_chan_t *ldcp)
{
int rv = 0;
ldc_cnex_t *cinfo = &ldcssp->cinfo;
if (cinfo->dip == NULL) {
DWARN(ldcp->id,
"i_ldc_unregister_channel: cnex has not registered\n");
return (EAGAIN);
}
if (ldcp->tstate & TS_CNEX_RDY) {
rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR);
if (rv) {
if (rv != EAGAIN) {
DWARN(ldcp->id,
"i_ldc_unregister_channel: err removing "
"Rx intr\n");
return (rv);
}
if (ldcp->rx_intr_state != LDC_INTR_PEND)
return (rv);
(void) i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
rv = cinfo->rem_intr(cinfo->dip, ldcp->id,
CNEX_RX_INTR);
if (rv) {
DWARN(ldcp->id, "i_ldc_unregister_channel: "
"err removing Rx interrupt\n");
return (rv);
}
}
rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR);
if (rv) {
DWARN(ldcp->id,
"i_ldc_unregister_channel: err removing Tx intr\n");
return (rv);
}
rv = cinfo->unreg_chan(ldcssp->cinfo.dip, ldcp->id);
if (rv) {
DWARN(ldcp->id,
"i_ldc_unregister_channel: cannot unreg channel\n");
return (rv);
}
ldcp->tstate &= ~TS_CNEX_RDY;
}
return (0);
}
static uint_t
i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2)
{
_NOTE(ARGUNUSED(arg2))
int rv;
ldc_chan_t *ldcp;
boolean_t notify_client = B_FALSE;
uint64_t notify_event = 0, link_state;
ASSERT(arg1 != NULL);
ldcp = (ldc_chan_t *)arg1;
D1(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) Received intr, ldcp=0x%p\n",
ldcp->id, ldcp);
mutex_enter(&ldcp->lock);
mutex_enter(&ldcp->tx_lock);
ldcp->tx_intr_state = LDC_INTR_ACTIVE;
link_state = ldcp->link_state;
rv = hv_ldc_tx_get_state(ldcp->id, &ldcp->tx_head, &ldcp->tx_tail,
&ldcp->link_state);
if (rv) {
cmn_err(CE_WARN,
"i_ldc_tx_hdlr: (0x%lx) cannot read queue ptrs rv=0x%d\n",
ldcp->id, rv);
i_ldc_clear_intr(ldcp, CNEX_TX_INTR);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (DDI_INTR_CLAIMED);
}
if (link_state != ldcp->link_state &&
ldcp->link_state == LDC_CHANNEL_DOWN) {
D1(ldcp->id, "i_ldc_tx_hdlr: channel link down\n", ldcp->id);
i_ldc_reset(ldcp, B_FALSE);
notify_client = B_TRUE;
notify_event = LDC_EVT_DOWN;
}
if (link_state != ldcp->link_state &&
ldcp->link_state == LDC_CHANNEL_RESET) {
D1(ldcp->id, "i_ldc_tx_hdlr: channel link reset\n", ldcp->id);
i_ldc_reset(ldcp, B_FALSE);
notify_client = B_TRUE;
notify_event = LDC_EVT_RESET;
}
if (link_state != ldcp->link_state &&
(ldcp->tstate & ~TS_IN_RESET) == TS_OPEN &&
ldcp->link_state == LDC_CHANNEL_UP) {
D1(ldcp->id, "i_ldc_tx_hdlr: channel link up\n", ldcp->id);
notify_client = B_TRUE;
notify_event = LDC_EVT_RESET;
ldcp->tstate |= TS_LINK_READY;
ldcp->status = LDC_READY;
}
if (!ldcp->cb_enabled)
notify_client = B_FALSE;
i_ldc_clear_intr(ldcp, CNEX_TX_INTR);
mutex_exit(&ldcp->tx_lock);
if (notify_client) {
ldcp->cb_inprogress = B_TRUE;
mutex_exit(&ldcp->lock);
rv = ldcp->cb(notify_event, ldcp->cb_arg);
if (rv) {
DWARN(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) callback "
"failure", ldcp->id);
}
mutex_enter(&ldcp->lock);
ldcp->cb_inprogress = B_FALSE;
}
mutex_exit(&ldcp->lock);
D1(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) exiting handler", ldcp->id);
return (DDI_INTR_CLAIMED);
}
static uint_t
i_ldc_rx_process_hvq(ldc_chan_t *ldcp, boolean_t *notify_client,
uint64_t *notify_event)
{
int rv;
uint64_t rx_head, rx_tail;
ldc_msg_t *msg;
uint64_t link_state, first_fragment = 0;
boolean_t trace_length = B_TRUE;
ASSERT(MUTEX_HELD(&ldcp->lock));
*notify_client = B_FALSE;
*notify_event = 0;
for (;;) {
link_state = ldcp->link_state;
rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
&ldcp->link_state);
if (rv) {
cmn_err(CE_WARN,
"i_ldc_rx_process_hvq: (0x%lx) cannot read "
"queue ptrs, rv=0x%d\n", ldcp->id, rv);
i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
return (EIO);
}
if (link_state != ldcp->link_state) {
switch (ldcp->link_state) {
case LDC_CHANNEL_DOWN:
D1(ldcp->id, "i_ldc_rx_process_hvq: channel "
"link down\n", ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->tx_lock);
*notify_client = B_TRUE;
*notify_event = LDC_EVT_DOWN;
goto loop_exit;
case LDC_CHANNEL_UP:
D1(ldcp->id, "i_ldc_rx_process_hvq: "
"channel link up\n", ldcp->id);
if ((ldcp->tstate & ~TS_IN_RESET) == TS_OPEN) {
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
ldcp->tstate |= TS_LINK_READY;
ldcp->status = LDC_READY;
}
break;
case LDC_CHANNEL_RESET:
default:
#ifdef DEBUG
force_reset:
#endif
D1(ldcp->id, "i_ldc_rx_process_hvq: channel "
"link reset\n", ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->tx_lock);
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
break;
}
}
#ifdef DEBUG
if (LDC_INJECT_RESET(ldcp))
goto force_reset;
if (LDC_INJECT_DRNGCLEAR(ldcp))
i_ldc_mem_inject_dring_clear(ldcp);
#endif
if (trace_length) {
TRACE_RXHVQ_LENGTH(ldcp, rx_head, rx_tail);
trace_length = B_FALSE;
}
if (rx_head == rx_tail) {
D2(ldcp->id, "i_ldc_rx_process_hvq: (0x%llx) "
"No packets\n", ldcp->id);
break;
}
D2(ldcp->id, "i_ldc_rx_process_hvq: head=0x%llx, "
"tail=0x%llx\n", rx_head, rx_tail);
DUMP_LDC_PKT(ldcp, "i_ldc_rx_process_hvq rcd",
ldcp->rx_q_va + rx_head);
msg = (ldc_msg_t *)(ldcp->rx_q_va + rx_head);
if (ldcp->mode == LDC_MODE_RAW) {
*notify_client = B_TRUE;
*notify_event |= LDC_EVT_READ;
break;
}
if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) {
if ((ldcp->tstate & ~TS_IN_RESET) != TS_UP) {
rx_head = (rx_head + LDC_PACKET_SIZE) %
(ldcp->rx_q_entries << LDC_PACKET_SHIFT);
if (rv = i_ldc_set_rx_head(ldcp, rx_head))
break;
continue;
} else {
uint64_t dq_head, dq_tail;
if (ldcp->mode != LDC_MODE_RELIABLE) {
if ((ldcp->tstate & TS_IN_RESET) == 0)
*notify_client = B_TRUE;
*notify_event |= LDC_EVT_READ;
break;
}
(void) i_ldc_dq_rx_get_state(ldcp, &dq_head,
&dq_tail, NULL);
dq_tail = (dq_tail + LDC_PACKET_SIZE) %
(ldcp->rx_dq_entries << LDC_PACKET_SHIFT);
if (dq_tail == dq_head ||
LDC_INJECT_DQFULL(ldcp)) {
rv = ENOSPC;
break;
}
}
}
rv = i_ldc_check_seqid(ldcp, msg);
if (rv != 0) {
DWARN(ldcp->id, "i_ldc_rx_process_hvq: (0x%llx) "
"seqid error, q_ptrs=0x%lx,0x%lx", ldcp->id,
rx_head, rx_tail);
if (first_fragment != 0) {
ldcp->last_msg_rcd = first_fragment - 1;
first_fragment = 0;
}
rv = i_ldc_send_pkt(ldcp, msg->type, LDC_NACK,
(msg->ctrl & LDC_CTRL_MASK));
if (rv) {
cmn_err(CE_NOTE, "i_ldc_rx_process_hvq: "
"(0x%lx) err sending CTRL/DATA NACK msg\n",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
break;
}
(void) i_ldc_set_rx_head(ldcp, rx_tail);
break;
}
ldcp->last_msg_rcd = msg->seqid;
if (msg->type & LDC_CTRL) {
uint64_t tstate = ldcp->tstate;
rv = i_ldc_ctrlmsg(ldcp, msg);
if (rv == EAGAIN) {
continue;
}
if (rv == ECONNRESET) {
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
break;
}
if (rv == 0 && ldcp->tstate == TS_UP &&
(tstate & ~TS_IN_RESET) !=
(ldcp->tstate & ~TS_IN_RESET)) {
*notify_client = B_TRUE;
*notify_event = LDC_EVT_UP;
}
}
if ((msg->type & LDC_DATA) && (msg->stype & LDC_NACK)) {
DWARN(ldcp->id,
"i_ldc_rx_process_hvq: (0x%llx) received DATA/NACK",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
break;
}
if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
if (rv = i_ldc_process_data_ACK(ldcp, msg)) {
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
break;
}
}
if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) {
ASSERT(ldcp->mode == LDC_MODE_RELIABLE);
i_ldc_rxdq_copy(ldcp, &rx_head);
if ((ldcp->tstate & TS_IN_RESET) == 0)
*notify_client = B_TRUE;
*notify_event |= LDC_EVT_READ;
} else {
rx_head = (rx_head + LDC_PACKET_SIZE) %
(ldcp->rx_q_entries << LDC_PACKET_SHIFT);
}
if (rv = i_ldc_set_rx_head(ldcp, rx_head)) {
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
break;
}
}
loop_exit:
if (ldcp->mode == LDC_MODE_RELIABLE) {
if ((*notify_event &
(LDC_EVT_READ | LDC_EVT_RESET)) == LDC_EVT_READ) {
int ack_rv;
ack_rv = i_ldc_send_pkt(ldcp, LDC_DATA, LDC_ACK, 0);
if (ack_rv && ack_rv != EWOULDBLOCK) {
cmn_err(CE_NOTE,
"i_ldc_rx_process_hvq: (0x%lx) cannot "
"send ACK\n", ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->tx_lock);
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
goto skip_ackpeek;
}
}
if (rv == ENOSPC) {
if (i_ldc_rx_ackpeek(ldcp, rx_head, rx_tail) != 0) {
ldcp->rx_ack_head = ACKPEEK_HEAD_INVALID;
*notify_client = B_TRUE;
*notify_event = LDC_EVT_RESET;
}
return (rv);
} else {
ldcp->rx_ack_head = ACKPEEK_HEAD_INVALID;
}
}
skip_ackpeek:
if ((*notify_event & (LDC_EVT_READ | LDC_EVT_RESET)) == LDC_EVT_READ)
return (0);
return (ENOMSG);
}
int
i_ldc_rx_ackpeek(ldc_chan_t *ldcp, uint64_t rx_head, uint64_t rx_tail)
{
int rv = 0;
ldc_msg_t *msg;
if (ldcp->rx_ack_head == ACKPEEK_HEAD_INVALID)
ldcp->rx_ack_head = rx_head;
while (ldcp->rx_ack_head != rx_tail) {
msg = (ldc_msg_t *)(ldcp->rx_q_va + ldcp->rx_ack_head);
if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
if (rv = i_ldc_process_data_ACK(ldcp, msg))
break;
msg->stype &= ~LDC_ACK;
}
ldcp->rx_ack_head =
(ldcp->rx_ack_head + LDC_PACKET_SIZE) %
(ldcp->rx_q_entries << LDC_PACKET_SHIFT);
}
return (rv);
}
int
ldc_init(uint64_t id, ldc_attr_t *attr, ldc_handle_t *handle)
{
ldc_chan_t *ldcp;
int rv, exit_val;
uint64_t ra_base, nentries;
uint64_t qlen;
exit_val = EINVAL;
if (attr == NULL) {
DWARN(id, "ldc_init: (0x%llx) invalid attr\n", id);
return (EINVAL);
}
if (handle == NULL) {
DWARN(id, "ldc_init: (0x%llx) invalid handle\n", id);
return (EINVAL);
}
rv = hv_ldc_tx_qinfo(id, &ra_base, &nentries);
if (rv == H_ECHANNEL) {
DWARN(id, "ldc_init: (0x%llx) invalid channel id\n", id);
return (EINVAL);
}
mutex_enter(&ldcssp->lock);
ldcp = ldcssp->chan_list;
while (ldcp != NULL) {
if (ldcp->id == id) {
DWARN(id, "ldc_init: (0x%llx) already initialized\n",
id);
mutex_exit(&ldcssp->lock);
return (EADDRINUSE);
}
ldcp = ldcp->next;
}
mutex_exit(&ldcssp->lock);
ASSERT(ldcp == NULL);
*handle = 0;
ldcp = kmem_zalloc(sizeof (ldc_chan_t), KM_SLEEP);
mutex_init(&ldcp->lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&ldcp->tx_lock, NULL, MUTEX_DRIVER, NULL);
ldcp->id = id;
ldcp->cb = NULL;
ldcp->cb_arg = NULL;
ldcp->cb_inprogress = B_FALSE;
ldcp->cb_enabled = B_FALSE;
ldcp->next = NULL;
ldcp->mode = attr->mode;
ldcp->devclass = attr->devclass;
ldcp->devinst = attr->instance;
ldcp->mtu = (attr->mtu > 0) ? attr->mtu : LDC_DEFAULT_MTU;
D1(ldcp->id,
"ldc_init: (0x%llx) channel attributes, class=0x%x, "
"instance=0x%llx, mode=%d, mtu=%d\n",
ldcp->id, ldcp->devclass, ldcp->devinst, ldcp->mode, ldcp->mtu);
ldcp->next_vidx = 0;
ldcp->tstate = TS_IN_RESET;
ldcp->hstate = 0;
ldcp->last_msg_snt = LDC_INIT_SEQID;
ldcp->last_ack_rcd = 0;
ldcp->last_msg_rcd = 0;
ldcp->rx_ack_head = ACKPEEK_HEAD_INVALID;
ldcp->stream_bufferp = NULL;
ldcp->exp_dring_list = NULL;
ldcp->imp_dring_list = NULL;
ldcp->mhdl_list = NULL;
ldcp->tx_intr_state = LDC_INTR_NONE;
ldcp->rx_intr_state = LDC_INTR_NONE;
switch (ldcp->mode) {
case LDC_MODE_RAW:
ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RAW;
ldcp->read_p = i_ldc_read_raw;
ldcp->write_p = i_ldc_write_raw;
break;
case LDC_MODE_UNRELIABLE:
ldcp->pkt_payload = LDC_PAYLOAD_SIZE_UNRELIABLE;
ldcp->read_p = i_ldc_read_packet;
ldcp->write_p = i_ldc_write_packet;
break;
case LDC_MODE_RELIABLE:
ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RELIABLE;
ldcp->stream_remains = 0;
ldcp->stream_offset = 0;
ldcp->stream_bufferp = kmem_alloc(ldcp->mtu, KM_SLEEP);
ldcp->read_p = i_ldc_read_stream;
ldcp->write_p = i_ldc_write_stream;
break;
default:
exit_val = EINVAL;
goto cleanup_on_exit;
}
qlen = (ldcp->mtu * ldc_mtu_msgs) / ldcp->pkt_payload;
if (!ISP2(qlen)) {
uint64_t tmp = 1;
while (qlen) {
qlen >>= 1; tmp <<= 1;
}
qlen = tmp;
}
ldcp->rx_q_entries =
(qlen < ldc_queue_entries) ? ldc_queue_entries : qlen;
ldcp->tx_q_entries = ldcp->rx_q_entries;
D1(ldcp->id, "ldc_init: queue length = 0x%llx\n", ldcp->rx_q_entries);
ldcp->tx_q_va = (uint64_t)
contig_mem_alloc(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
if (ldcp->tx_q_va == 0) {
cmn_err(CE_WARN,
"ldc_init: (0x%lx) TX queue allocation failed\n",
ldcp->id);
exit_val = ENOMEM;
goto cleanup_on_exit;
}
ldcp->tx_q_ra = va_to_pa((caddr_t)ldcp->tx_q_va);
D2(ldcp->id, "ldc_init: txq_va=0x%llx, txq_ra=0x%llx, entries=0x%llx\n",
ldcp->tx_q_va, ldcp->tx_q_ra, ldcp->tx_q_entries);
ldcp->tstate |= TS_TXQ_RDY;
ldcp->rx_q_va = (uint64_t)
contig_mem_alloc(ldcp->rx_q_entries << LDC_PACKET_SHIFT);
if (ldcp->rx_q_va == 0) {
cmn_err(CE_WARN,
"ldc_init: (0x%lx) RX queue allocation failed\n",
ldcp->id);
exit_val = ENOMEM;
goto cleanup_on_exit;
}
ldcp->rx_q_ra = va_to_pa((caddr_t)ldcp->rx_q_va);
D2(ldcp->id, "ldc_init: rxq_va=0x%llx, rxq_ra=0x%llx, entries=0x%llx\n",
ldcp->rx_q_va, ldcp->rx_q_ra, ldcp->rx_q_entries);
ldcp->tstate |= TS_RXQ_RDY;
if (ldcp->mode == LDC_MODE_RELIABLE) {
ldcp->readq_get_state = i_ldc_dq_rx_get_state;
ldcp->readq_set_head = i_ldc_set_rxdq_head;
if (!ISP2(ldc_rxdq_multiplier)) {
D1(ldcp->id, "ldc_init: (0x%llx) ldc_rxdq_multiplier "
"not a power of 2, resetting", ldcp->id);
ldc_rxdq_multiplier = LDC_RXDQ_MULTIPLIER;
}
ldcp->rx_dq_entries = ldc_rxdq_multiplier * ldcp->rx_q_entries;
ldcp->rx_dq_va = (uint64_t)
kmem_alloc(ldcp->rx_dq_entries << LDC_PACKET_SHIFT,
KM_SLEEP);
if (ldcp->rx_dq_va == 0) {
cmn_err(CE_WARN,
"ldc_init: (0x%lx) RX data queue "
"allocation failed\n", ldcp->id);
exit_val = ENOMEM;
goto cleanup_on_exit;
}
ldcp->rx_dq_head = ldcp->rx_dq_tail = 0;
D2(ldcp->id, "ldc_init: rx_dq_va=0x%llx, "
"rx_dq_entries=0x%llx\n", ldcp->rx_dq_va,
ldcp->rx_dq_entries);
} else {
ldcp->readq_get_state = i_ldc_hvq_rx_get_state;
ldcp->readq_set_head = i_ldc_set_rx_head;
}
mutex_init(&ldcp->exp_dlist_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&ldcp->imp_dlist_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&ldcp->mlist_lock, NULL, MUTEX_DRIVER, NULL);
ldcp->status = LDC_INIT;
mutex_enter(&ldcssp->lock);
ldcp->next = ldcssp->chan_list;
ldcssp->chan_list = ldcp;
ldcssp->channel_count++;
mutex_exit(&ldcssp->lock);
*handle = (ldc_handle_t)ldcp;
D1(ldcp->id, "ldc_init: (0x%llx) channel initialized\n", ldcp->id);
return (0);
cleanup_on_exit:
if (ldcp->mode == LDC_MODE_RELIABLE && ldcp->stream_bufferp)
kmem_free(ldcp->stream_bufferp, ldcp->mtu);
if (ldcp->tstate & TS_TXQ_RDY)
contig_mem_free((caddr_t)ldcp->tx_q_va,
(ldcp->tx_q_entries << LDC_PACKET_SHIFT));
if (ldcp->tstate & TS_RXQ_RDY)
contig_mem_free((caddr_t)ldcp->rx_q_va,
(ldcp->rx_q_entries << LDC_PACKET_SHIFT));
mutex_destroy(&ldcp->tx_lock);
mutex_destroy(&ldcp->lock);
kmem_free(ldcp, sizeof (ldc_chan_t));
return (exit_val);
}
int
ldc_fini(ldc_handle_t handle)
{
ldc_chan_t *ldcp;
ldc_chan_t *tmp_ldcp;
uint64_t id;
if (handle == 0) {
DWARN(DBG_ALL_LDCS, "ldc_fini: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
id = ldcp->id;
mutex_enter(&ldcp->lock);
if ((ldcp->tstate & ~TS_IN_RESET) > TS_INIT) {
DWARN(ldcp->id, "ldc_fini: (0x%llx) channel is open\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EBUSY);
}
mutex_enter(&ldcssp->lock);
tmp_ldcp = ldcssp->chan_list;
if (tmp_ldcp == ldcp) {
ldcssp->chan_list = ldcp->next;
ldcp->next = NULL;
} else {
while (tmp_ldcp != NULL) {
if (tmp_ldcp->next == ldcp) {
tmp_ldcp->next = ldcp->next;
ldcp->next = NULL;
break;
}
tmp_ldcp = tmp_ldcp->next;
}
if (tmp_ldcp == NULL) {
DWARN(DBG_ALL_LDCS, "ldc_fini: invalid channel hdl\n");
mutex_exit(&ldcssp->lock);
mutex_exit(&ldcp->lock);
return (EINVAL);
}
}
ldcssp->channel_count--;
mutex_exit(&ldcssp->lock);
if (ldcp->mtbl) {
(void) hv_ldc_set_map_table(ldcp->id, 0, 0);
if (ldcp->mtbl->contigmem)
contig_mem_free(ldcp->mtbl->table, ldcp->mtbl->size);
else
kmem_free(ldcp->mtbl->table, ldcp->mtbl->size);
mutex_destroy(&ldcp->mtbl->lock);
kmem_free(ldcp->mtbl, sizeof (ldc_mtbl_t));
}
mutex_destroy(&ldcp->exp_dlist_lock);
mutex_destroy(&ldcp->imp_dlist_lock);
mutex_destroy(&ldcp->mlist_lock);
if (ldcp->mode == LDC_MODE_RELIABLE && ldcp->stream_bufferp)
kmem_free(ldcp->stream_bufferp, ldcp->mtu);
contig_mem_free((caddr_t)ldcp->rx_q_va,
(ldcp->rx_q_entries << LDC_PACKET_SHIFT));
ldcp->tstate &= ~TS_RXQ_RDY;
if (ldcp->mode == LDC_MODE_RELIABLE) {
kmem_free((caddr_t)ldcp->rx_dq_va,
(ldcp->rx_dq_entries << LDC_PACKET_SHIFT));
}
contig_mem_free((caddr_t)ldcp->tx_q_va,
(ldcp->tx_q_entries << LDC_PACKET_SHIFT));
ldcp->tstate &= ~TS_TXQ_RDY;
mutex_exit(&ldcp->lock);
mutex_destroy(&ldcp->tx_lock);
mutex_destroy(&ldcp->lock);
kmem_free(ldcp, sizeof (ldc_chan_t));
D1(id, "ldc_fini: (0x%llx) channel finalized\n", id);
return (0);
}
int
ldc_open(ldc_handle_t handle)
{
ldc_chan_t *ldcp;
int rv;
if (handle == 0) {
DWARN(DBG_ALL_LDCS, "ldc_open: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
if (ldcp->tstate < TS_INIT) {
DWARN(ldcp->id,
"ldc_open: (0x%llx) channel not initialized\n", ldcp->id);
mutex_exit(&ldcp->lock);
return (EFAULT);
}
if ((ldcp->tstate & ~TS_IN_RESET) >= TS_OPEN) {
DWARN(ldcp->id,
"ldc_open: (0x%llx) channel is already open\n", ldcp->id);
mutex_exit(&ldcp->lock);
return (EFAULT);
}
rv = hv_ldc_tx_qconf(ldcp->id, 0, 0);
if (rv) {
cmn_err(CE_WARN,
"ldc_open: (0x%lx) channel tx queue unconf failed\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EIO);
}
rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries);
if (rv) {
cmn_err(CE_WARN,
"ldc_open: (0x%lx) channel tx queue conf failed\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EIO);
}
D2(ldcp->id, "ldc_open: (0x%llx) registered tx queue with LDC\n",
ldcp->id);
rv = hv_ldc_rx_qconf(ldcp->id, 0, 0);
if (rv) {
cmn_err(CE_WARN,
"ldc_open: (0x%lx) channel rx queue unconf failed\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EIO);
}
rv = hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra, ldcp->rx_q_entries);
if (rv) {
cmn_err(CE_WARN,
"ldc_open: (0x%lx) channel rx queue conf failed\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EIO);
}
D2(ldcp->id, "ldc_open: (0x%llx) registered rx queue with LDC\n",
ldcp->id);
ldcp->tstate |= TS_QCONF_RDY;
rv = i_ldc_register_channel(ldcp);
if (rv && rv != EAGAIN) {
cmn_err(CE_WARN,
"ldc_open: (0x%lx) channel register failed\n", ldcp->id);
ldcp->tstate &= ~TS_QCONF_RDY;
(void) hv_ldc_tx_qconf(ldcp->id, 0, 0);
(void) hv_ldc_rx_qconf(ldcp->id, 0, 0);
mutex_exit(&ldcp->lock);
return (EIO);
}
ldcp->status = LDC_OPEN;
rv = hv_ldc_tx_get_state(ldcp->id,
&ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
if (rv) {
cmn_err(CE_WARN,
"ldc_open: (0x%lx) cannot read channel state\n",
ldcp->id);
(void) i_ldc_unregister_channel(ldcp);
ldcp->tstate &= ~TS_QCONF_RDY;
(void) hv_ldc_tx_qconf(ldcp->id, 0, 0);
(void) hv_ldc_rx_qconf(ldcp->id, 0, 0);
mutex_exit(&ldcp->lock);
return (EIO);
}
ldcp->tx_ackd_head = ldcp->tx_head;
if (ldcp->link_state == LDC_CHANNEL_UP ||
ldcp->link_state == LDC_CHANNEL_RESET) {
ldcp->tstate |= TS_LINK_READY;
ldcp->status = LDC_READY;
}
if (ldcp->mode == LDC_MODE_RAW) {
ldcp->tstate = TS_UP;
ldcp->status = LDC_UP;
}
mutex_exit(&ldcp->lock);
mutex_enter(&ldcssp->lock);
ldcssp->channels_open++;
mutex_exit(&ldcssp->lock);
D1(ldcp->id,
"ldc_open: (0x%llx) channel (0x%p) open for use "
"(tstate=0x%x, status=0x%x)\n",
ldcp->id, ldcp, ldcp->tstate, ldcp->status);
return (0);
}
int
ldc_close(ldc_handle_t handle)
{
ldc_chan_t *ldcp;
int rv = 0, retries = 0;
boolean_t chk_done = B_FALSE;
if (handle == 0) {
DWARN(DBG_ALL_LDCS, "ldc_close: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
if ((ldcp->tstate & ~TS_IN_RESET) < TS_OPEN) {
DWARN(ldcp->id,
"ldc_close: (0x%llx) channel is not open\n", ldcp->id);
mutex_exit(&ldcp->lock);
return (EFAULT);
}
if (ldcp->mhdl_list != NULL) {
DWARN(ldcp->id,
"ldc_close: (0x%llx) channel has bound memory handles\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EBUSY);
}
if (ldcp->exp_dring_list != NULL) {
DWARN(ldcp->id,
"ldc_close: (0x%llx) channel has bound descriptor rings\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EBUSY);
}
if (ldcp->imp_dring_list != NULL) {
DWARN(ldcp->id,
"ldc_close: (0x%llx) channel has mapped descriptor rings\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EBUSY);
}
if (ldcp->cb_inprogress) {
DWARN(ldcp->id, "ldc_close: (0x%llx) callback active\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EWOULDBLOCK);
}
mutex_enter(&ldcp->tx_lock);
for (;;) {
rv = hv_ldc_tx_get_state(ldcp->id,
&ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
if (rv) {
cmn_err(CE_WARN,
"ldc_close: (0x%lx) cannot read qptrs\n", ldcp->id);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (EIO);
}
if (ldcp->tx_head == ldcp->tx_tail ||
ldcp->link_state != LDC_CHANNEL_UP) {
break;
}
if (chk_done) {
DWARN(ldcp->id,
"ldc_close: (0x%llx) Tx queue drain timeout\n",
ldcp->id);
break;
}
delay(drv_usectohz(1000));
chk_done = B_TRUE;
}
(void) i_ldc_txq_reconf(ldcp);
i_ldc_rxq_drain(ldcp);
while ((rv = i_ldc_unregister_channel(ldcp)) != 0) {
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
if (rv != EAGAIN || retries >= ldc_max_retries) {
cmn_err(CE_WARN,
"ldc_close: (0x%lx) unregister failed, %d\n",
ldcp->id, rv);
return (rv);
}
drv_usecwait(ldc_close_delay);
mutex_enter(&ldcp->lock);
mutex_enter(&ldcp->tx_lock);
retries++;
}
ldcp->tstate &= ~TS_QCONF_RDY;
rv = hv_ldc_tx_qconf(ldcp->id, 0, 0);
if (rv) {
cmn_err(CE_WARN,
"ldc_close: (0x%lx) channel TX queue unconf failed\n",
ldcp->id);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (EIO);
}
rv = hv_ldc_rx_qconf(ldcp->id, 0, 0);
if (rv) {
cmn_err(CE_WARN,
"ldc_close: (0x%lx) channel RX queue unconf failed\n",
ldcp->id);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (EIO);
}
i_ldc_reset_state(ldcp);
ldcp->tx_ackd_head = 0;
ldcp->tx_head = 0;
ldcp->tstate = TS_IN_RESET|TS_INIT;
ldcp->status = LDC_INIT;
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
mutex_enter(&ldcssp->lock);
ldcssp->channels_open--;
mutex_exit(&ldcssp->lock);
D1(ldcp->id, "ldc_close: (0x%llx) channel closed\n", ldcp->id);
return (0);
}
int
ldc_reg_callback(ldc_handle_t handle,
uint_t(*cb)(uint64_t event, caddr_t arg), caddr_t arg)
{
ldc_chan_t *ldcp;
if (handle == 0) {
DWARN(DBG_ALL_LDCS,
"ldc_reg_callback: invalid channel handle\n");
return (EINVAL);
}
if (((uint64_t)cb) < KERNELBASE) {
DWARN(DBG_ALL_LDCS, "ldc_reg_callback: invalid callback\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
if (ldcp->cb) {
DWARN(ldcp->id, "ldc_reg_callback: (0x%llx) callback exists\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EIO);
}
if (ldcp->cb_inprogress) {
DWARN(ldcp->id, "ldc_reg_callback: (0x%llx) callback active\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EWOULDBLOCK);
}
ldcp->cb = cb;
ldcp->cb_arg = arg;
ldcp->cb_enabled = B_TRUE;
D1(ldcp->id,
"ldc_reg_callback: (0x%llx) registered callback for channel\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (0);
}
int
ldc_unreg_callback(ldc_handle_t handle)
{
ldc_chan_t *ldcp;
if (handle == 0) {
DWARN(DBG_ALL_LDCS,
"ldc_unreg_callback: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
if (ldcp->cb == NULL) {
DWARN(ldcp->id,
"ldc_unreg_callback: (0x%llx) no callback exists\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EIO);
}
if (ldcp->cb_inprogress) {
DWARN(ldcp->id,
"ldc_unreg_callback: (0x%llx) callback active\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EWOULDBLOCK);
}
ldcp->cb = NULL;
ldcp->cb_arg = NULL;
ldcp->cb_enabled = B_FALSE;
D1(ldcp->id,
"ldc_unreg_callback: (0x%llx) unregistered callback for channel\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (0);
}
int
ldc_up(ldc_handle_t handle)
{
int rv;
ldc_chan_t *ldcp;
ldc_msg_t *ldcmsg;
uint64_t tx_tail, tstate, link_state;
if (handle == 0) {
DWARN(DBG_ALL_LDCS, "ldc_up: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
D1(ldcp->id, "ldc_up: (0x%llx) doing channel UP\n", ldcp->id);
tstate = ldcp->tstate;
ldcp->tstate &= ~TS_IN_RESET;
if (ldcp->tstate == TS_UP) {
DWARN(ldcp->id,
"ldc_up: (0x%llx) channel is already in UP state\n",
ldcp->id);
ldcp->status = LDC_UP;
if ((tstate & TS_IN_RESET) &&
ldcp->rx_intr_state == LDC_INTR_PEND) {
D1(ldcp->id,
"ldc_up: (0x%llx) channel has pending data, "
"clearing interrupt\n", ldcp->id);
i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
}
mutex_exit(&ldcp->lock);
return (0);
}
if (ldcp->mode == LDC_MODE_RAW && ldcp->tstate >= TS_READY) {
ldcp->tstate = TS_UP;
mutex_exit(&ldcp->lock);
return (0);
}
if (ldcp->hstate) {
D1(ldcp->id,
"ldc_up: (0x%llx) channel handshake in progress\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (0);
}
mutex_enter(&ldcp->tx_lock);
link_state = ldcp->link_state;
rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
if (rv) {
D1(ldcp->id, "ldc_up: (0x%llx) cannot initiate handshake\n",
ldcp->id);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (ECONNREFUSED);
}
if ((link_state == LDC_CHANNEL_DOWN) &&
(link_state != ldcp->link_state)) {
ASSERT((ldcp->link_state == LDC_CHANNEL_RESET) ||
(ldcp->link_state == LDC_CHANNEL_UP));
if (ldcp->mode == LDC_MODE_RAW) {
ldcp->status = LDC_UP;
ldcp->tstate = TS_UP;
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (0);
} else {
ldcp->status = LDC_READY;
ldcp->tstate |= TS_LINK_READY;
}
}
ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
ZERO_PKT(ldcmsg);
ldcmsg->type = LDC_CTRL;
ldcmsg->stype = LDC_INFO;
ldcmsg->ctrl = LDC_VER;
ldcp->next_vidx = 0;
bcopy(&ldc_versions[0], ldcmsg->udata, sizeof (ldc_versions[0]));
DUMP_LDC_PKT(ldcp, "ldc_up snd ver", (uint64_t)ldcmsg);
tx_tail = (tx_tail + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
rv = i_ldc_set_tx_tail(ldcp, tx_tail);
if (rv) {
DWARN(ldcp->id,
"ldc_up: (0x%llx) cannot initiate handshake rv=%d\n",
ldcp->id, rv);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (rv);
}
ldcp->hstate |= TS_SENT_VER;
ldcp->tx_tail = tx_tail;
D1(ldcp->id, "ldc_up: (0x%llx) channel up initiated\n", ldcp->id);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (rv);
}
int
ldc_down(ldc_handle_t handle)
{
ldc_chan_t *ldcp;
if (handle == 0) {
DWARN(DBG_ALL_LDCS, "ldc_down: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (0);
}
int
ldc_status(ldc_handle_t handle, ldc_status_t *status)
{
ldc_chan_t *ldcp;
if (handle == 0 || status == NULL) {
DWARN(DBG_ALL_LDCS, "ldc_status: invalid argument\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
*status = ((ldc_chan_t *)handle)->status;
D1(ldcp->id,
"ldc_status: (0x%llx) returned status %d\n", ldcp->id, *status);
return (0);
}
int
ldc_set_cb_mode(ldc_handle_t handle, ldc_cb_mode_t cmode)
{
ldc_chan_t *ldcp;
if (handle == 0) {
DWARN(DBG_ALL_LDCS,
"ldc_set_intr_mode: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
switch (cmode) {
case LDC_CB_DISABLE:
if (!ldcp->cb_enabled) {
DWARN(ldcp->id,
"ldc_set_cb_mode: (0x%llx) callbacks disabled\n",
ldcp->id);
break;
}
ldcp->cb_enabled = B_FALSE;
D1(ldcp->id, "ldc_set_cb_mode: (0x%llx) disabled callbacks\n",
ldcp->id);
break;
case LDC_CB_ENABLE:
if (ldcp->cb_enabled) {
DWARN(ldcp->id,
"ldc_set_cb_mode: (0x%llx) callbacks enabled\n",
ldcp->id);
break;
}
ldcp->cb_enabled = B_TRUE;
D1(ldcp->id, "ldc_set_cb_mode: (0x%llx) enabled callbacks\n",
ldcp->id);
break;
}
mutex_exit(&ldcp->lock);
return (0);
}
int
ldc_chkq(ldc_handle_t handle, boolean_t *hasdata)
{
int rv;
uint64_t rx_head, rx_tail;
ldc_chan_t *ldcp;
if (handle == 0) {
DWARN(DBG_ALL_LDCS, "ldc_chkq: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
*hasdata = B_FALSE;
mutex_enter(&ldcp->lock);
if (ldcp->tstate != TS_UP) {
D1(ldcp->id,
"ldc_chkq: (0x%llx) channel is not up\n", ldcp->id);
mutex_exit(&ldcp->lock);
return (ECONNRESET);
}
rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
&ldcp->link_state);
if (rv != 0) {
cmn_err(CE_WARN,
"ldc_chkq: (0x%lx) unable to read queue ptrs", ldcp->id);
mutex_exit(&ldcp->lock);
return (EIO);
}
if (ldcp->link_state == LDC_CHANNEL_DOWN ||
ldcp->link_state == LDC_CHANNEL_RESET) {
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (ECONNRESET);
}
switch (ldcp->mode) {
case LDC_MODE_RAW:
*hasdata = (rx_head != rx_tail);
break;
case LDC_MODE_UNRELIABLE:
if (rx_head != rx_tail) {
*hasdata = (i_ldc_chkq(ldcp) == 0);
if (*hasdata == B_FALSE &&
ldcp->rx_intr_state == LDC_INTR_PEND) {
i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
}
}
break;
case LDC_MODE_RELIABLE:
if (ldcp->stream_remains > 0)
*hasdata = B_TRUE;
else
*hasdata = (ldcp->rx_dq_head != ldcp->rx_dq_tail);
break;
default:
cmn_err(CE_WARN, "ldc_chkq: (0x%lx) unexpected channel mode "
"(0x%x)", ldcp->id, ldcp->mode);
mutex_exit(&ldcp->lock);
return (EIO);
}
mutex_exit(&ldcp->lock);
return (0);
}
int
ldc_read(ldc_handle_t handle, caddr_t bufp, size_t *sizep)
{
ldc_chan_t *ldcp;
uint64_t rx_head = 0, rx_tail = 0;
int rv = 0, exit_val;
if (handle == 0) {
DWARN(DBG_ALL_LDCS, "ldc_read: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
if (ldcp->tstate != TS_UP) {
DWARN(ldcp->id,
"ldc_read: (0x%llx) channel is not in UP state\n",
ldcp->id);
exit_val = ECONNRESET;
} else if (ldcp->mode == LDC_MODE_RELIABLE) {
TRACE_RXDQ_LENGTH(ldcp);
exit_val = ldcp->read_p(ldcp, bufp, sizep);
if (ldcp->rx_intr_state == LDC_INTR_PEND &&
Q_CONTIG_SPACE(ldcp->rx_dq_head, ldcp->rx_dq_tail,
ldcp->rx_dq_entries << LDC_PACKET_SHIFT) >=
LDC_PACKET_SIZE) {
i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
}
mutex_exit(&ldcp->lock);
return (exit_val);
} else {
exit_val = ldcp->read_p(ldcp, bufp, sizep);
}
rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
&ldcp->link_state);
if (rv != 0) {
cmn_err(CE_WARN, "ldc_read: (0x%lx) unable to read queue ptrs",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
mutex_exit(&ldcp->lock);
return (ECONNRESET);
}
if (exit_val == 0) {
if (ldcp->link_state == LDC_CHANNEL_DOWN ||
ldcp->link_state == LDC_CHANNEL_RESET) {
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
exit_val = ECONNRESET;
mutex_exit(&ldcp->tx_lock);
}
if ((rv == 0) &&
(ldcp->rx_intr_state == LDC_INTR_PEND) &&
(rx_head == rx_tail)) {
i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
}
}
mutex_exit(&ldcp->lock);
return (exit_val);
}
static int
i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
{
uint64_t q_size_mask;
ldc_msg_t *msgp;
uint8_t *msgbufp;
int rv = 0, space;
uint64_t rx_head, rx_tail;
space = *sizep;
if (space < LDC_PAYLOAD_SIZE_RAW)
return (ENOBUFS);
ASSERT(mutex_owned(&ldcp->lock));
q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT;
rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
&ldcp->link_state);
if (rv != 0) {
cmn_err(CE_WARN,
"ldc_read_raw: (0x%lx) unable to read queue ptrs",
ldcp->id);
return (EIO);
}
D1(ldcp->id, "ldc_read_raw: (0x%llx) rxh=0x%llx,"
" rxt=0x%llx, st=0x%llx\n",
ldcp->id, rx_head, rx_tail, ldcp->link_state);
if (ldcp->link_state == LDC_CHANNEL_DOWN ||
ldcp->link_state == LDC_CHANNEL_RESET) {
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
if (rx_head == rx_tail) {
*sizep = 0;
return (0);
}
msgp = (ldc_msg_t *)(ldcp->rx_q_va + rx_head);
msgbufp = (uint8_t *)&(msgp->raw[0]);
bcopy(msgbufp, target_bufp, LDC_PAYLOAD_SIZE_RAW);
DUMP_PAYLOAD(ldcp->id, msgbufp);
*sizep = LDC_PAYLOAD_SIZE_RAW;
rx_head = (rx_head + LDC_PACKET_SIZE) & q_size_mask;
rv = i_ldc_set_rx_head(ldcp, rx_head);
return (rv);
}
static int
i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
{
int rv = 0;
uint64_t rx_head = 0, rx_tail = 0;
uint64_t curr_head = 0;
ldc_msg_t *msg;
caddr_t target;
size_t len = 0, bytes_read = 0;
int retries = 0;
uint64_t q_va, q_size_mask;
uint64_t first_fragment = 0;
target = target_bufp;
ASSERT(mutex_owned(&ldcp->lock));
if (target_bufp == NULL || *sizep == 0) {
DWARN(ldcp->id, "ldc_read: (0x%llx) invalid buffer/size\n",
ldcp->id);
return (EINVAL);
}
if (ldcp->mode == LDC_MODE_RELIABLE) {
q_va = ldcp->rx_dq_va;
q_size_mask = (ldcp->rx_dq_entries-1)<<LDC_PACKET_SHIFT;
} else {
q_va = ldcp->rx_q_va;
q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT;
}
rv = ldcp->readq_get_state(ldcp, &curr_head, &rx_tail,
&ldcp->link_state);
if (rv != 0) {
cmn_err(CE_WARN, "ldc_read: (0x%lx) unable to read queue ptrs",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
D1(ldcp->id, "ldc_read: (0x%llx) chd=0x%llx, tl=0x%llx, st=0x%llx\n",
ldcp->id, curr_head, rx_tail, ldcp->link_state);
if (ldcp->link_state != LDC_CHANNEL_UP)
goto channel_is_reset;
for (;;) {
if (curr_head == rx_tail) {
if (ldcp->mode == LDC_MODE_RELIABLE)
(void) i_ldc_chkq(ldcp);
rv = ldcp->readq_get_state(ldcp,
&rx_head, &rx_tail, &ldcp->link_state);
if (rv != 0) {
cmn_err(CE_WARN,
"ldc_read: (0x%lx) cannot read queue ptrs",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
if (ldcp->link_state != LDC_CHANNEL_UP)
goto channel_is_reset;
if (curr_head == rx_tail) {
if (first_fragment != 0) {
drv_usecwait(ldc_delay);
if (++retries < ldc_max_retries)
continue;
*sizep = 0;
if (ldcp->mode != LDC_MODE_RELIABLE)
ldcp->last_msg_rcd =
first_fragment - 1;
DWARN(DBG_ALL_LDCS, "ldc_read: "
"(0x%llx) read timeout", ldcp->id);
return (EAGAIN);
}
*sizep = 0;
break;
}
}
retries = 0;
D2(ldcp->id,
"ldc_read: (0x%llx) chd=0x%llx, rxhd=0x%llx, rxtl=0x%llx\n",
ldcp->id, curr_head, rx_head, rx_tail);
msg = (ldc_msg_t *)(q_va + curr_head);
DUMP_LDC_PKT(ldcp, "ldc_read received pkt",
ldcp->rx_q_va + curr_head);
if (ldcp->mode != LDC_MODE_RELIABLE) {
if ((rv = i_ldc_check_seqid(ldcp, msg)) != 0) {
DWARN(ldcp->id, "ldc_read: (0x%llx) seqid "
"error, q_ptrs=0x%lx,0x%lx",
ldcp->id, rx_head, rx_tail);
bytes_read = 0;
if (first_fragment != 0) {
ldcp->last_msg_rcd = first_fragment - 1;
first_fragment = 0;
}
rv = i_ldc_send_pkt(ldcp, msg->type, LDC_NACK,
(msg->ctrl & LDC_CTRL_MASK));
if (rv) {
cmn_err(CE_NOTE,
"ldc_read: (0x%lx) err sending "
"NACK msg\n", ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->tx_lock);
rv = ECONNRESET;
break;
}
rv = i_ldc_set_rx_head(ldcp, rx_tail);
break;
}
if (msg->type & LDC_CTRL) {
if (rv = i_ldc_ctrlmsg(ldcp, msg)) {
if (rv == EAGAIN)
continue;
rv = i_ldc_set_rx_head(ldcp, rx_tail);
*sizep = 0;
bytes_read = 0;
break;
}
}
if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
if (rv = i_ldc_process_data_ACK(ldcp, msg)) {
*sizep = 0;
bytes_read = 0;
break;
}
}
if ((msg->type & LDC_DATA) && (msg->stype & LDC_NACK)) {
DWARN(ldcp->id,
"ldc_read: (0x%llx) received DATA/NACK",
ldcp->id);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_TRUE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
}
if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) {
uint8_t *msgbuf = (uint8_t *)(
(ldcp->mode == LDC_MODE_RELIABLE) ?
msg->rdata : msg->udata);
D2(ldcp->id,
"ldc_read: (0x%llx) received data msg\n", ldcp->id);
len = (msg->env & LDC_LEN_MASK);
if (first_fragment == 0) {
if (!(msg->env & LDC_FRAG_START)) {
DWARN(DBG_ALL_LDCS,
"ldc_read: (0x%llx) not start - "
"frag=%x\n", ldcp->id,
(msg->env) & LDC_FRAG_MASK);
bytes_read = 0;
target = target_bufp;
curr_head =
(curr_head + LDC_PACKET_SIZE)
& q_size_mask;
if (rv = ldcp->readq_set_head(ldcp,
curr_head))
break;
continue;
}
first_fragment = msg->seqid;
} else {
if (msg->env & LDC_FRAG_START) {
DWARN(DBG_ALL_LDCS,
"ldc_read:(0x%llx) unexpected pkt"
" env=0x%x discarding %d bytes,"
" lastmsg=%d, currentmsg=%d\n",
ldcp->id, msg->env&LDC_FRAG_MASK,
bytes_read, ldcp->last_msg_rcd,
msg->seqid);
bytes_read = 0;
target = target_bufp;
first_fragment = msg->seqid;
if (rv = ldcp->readq_set_head(ldcp,
curr_head))
break;
}
}
if (len <= (*sizep - bytes_read)) {
bcopy(msgbuf, target, len);
target += len;
bytes_read += len;
} else {
DWARN(DBG_ALL_LDCS,
"ldc_read: (0x%llx) buffer too small, "
"head=0x%lx, expect=%d, got=%d\n", ldcp->id,
curr_head, *sizep, bytes_read+len);
first_fragment = 0;
target = target_bufp;
bytes_read = 0;
if (rv = ldcp->readq_set_head(ldcp, curr_head))
break;
continue;
}
}
if (ldcp->mode != LDC_MODE_RELIABLE)
ldcp->last_msg_rcd = msg->seqid;
curr_head = (curr_head + LDC_PACKET_SIZE) & q_size_mask;
if (msg->env & LDC_FRAG_STOP) {
if (rv = ldcp->readq_set_head(ldcp, curr_head))
bytes_read = 0;
*sizep = bytes_read;
break;
}
if ((msg->type & LDC_CTRL) ||
((msg->type & LDC_DATA) && (msg->stype & LDC_ACK))) {
if (rv = ldcp->readq_set_head(ldcp, curr_head)) {
bytes_read = 0;
break;
}
D2(ldcp->id, "ldc_read: (0x%llx) set ACK qhead 0x%llx",
ldcp->id, curr_head);
}
}
D2(ldcp->id, "ldc_read: (0x%llx) end size=%d", ldcp->id, *sizep);
return (rv);
channel_is_reset:
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->tx_lock);
return (ECONNRESET);
}
static int
i_ldc_read_stream(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
{
int rv;
size_t size;
ASSERT(mutex_owned(&ldcp->lock));
D2(ldcp->id, "i_ldc_read_stream: (0x%llx) buffer size=%d",
ldcp->id, *sizep);
if (ldcp->stream_remains == 0) {
size = ldcp->mtu;
rv = i_ldc_read_packet(ldcp,
(caddr_t)ldcp->stream_bufferp, &size);
D2(ldcp->id, "i_ldc_read_stream: read packet (0x%llx) size=%d",
ldcp->id, size);
if (rv != 0)
return (rv);
ldcp->stream_remains = size;
ldcp->stream_offset = 0;
}
size = MIN(ldcp->stream_remains, *sizep);
bcopy(ldcp->stream_bufferp + ldcp->stream_offset, target_bufp, size);
ldcp->stream_offset += size;
ldcp->stream_remains -= size;
D2(ldcp->id, "i_ldc_read_stream: (0x%llx) fill from buffer size=%d",
ldcp->id, size);
*sizep = size;
return (0);
}
int
ldc_write(ldc_handle_t handle, caddr_t buf, size_t *sizep)
{
ldc_chan_t *ldcp;
int rv = 0;
if (handle == 0) {
DWARN(DBG_ALL_LDCS, "ldc_write: invalid channel handle\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->tx_lock);
if (buf == NULL || sizep == NULL) {
DWARN(ldcp->id, "ldc_write: (0x%llx) invalid data write\n",
ldcp->id);
mutex_exit(&ldcp->tx_lock);
return (EINVAL);
}
if (*sizep == 0) {
DWARN(ldcp->id, "ldc_write: (0x%llx) write size of zero\n",
ldcp->id);
mutex_exit(&ldcp->tx_lock);
return (0);
}
if (ldcp->tstate != TS_UP) {
DWARN(ldcp->id,
"ldc_write: (0x%llx) channel is not in UP state\n",
ldcp->id);
*sizep = 0;
rv = ECONNRESET;
} else {
rv = ldcp->write_p(ldcp, buf, sizep);
}
mutex_exit(&ldcp->tx_lock);
return (rv);
}
static int
i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep)
{
ldc_msg_t *ldcmsg;
uint64_t tx_head, tx_tail, new_tail;
int rv = 0;
size_t size;
ASSERT(MUTEX_HELD(&ldcp->tx_lock));
ASSERT(ldcp->mode == LDC_MODE_RAW);
size = *sizep;
if (size > ldcp->pkt_payload) {
DWARN(ldcp->id,
"ldc_write: (0x%llx) invalid size (0x%llx) for RAW mode\n",
ldcp->id, *sizep);
*sizep = 0;
return (EMSGSIZE);
}
rv = hv_ldc_tx_get_state(ldcp->id,
&ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
if (rv != 0) {
cmn_err(CE_WARN,
"ldc_write: (0x%lx) cannot read queue ptrs\n", ldcp->id);
*sizep = 0;
return (EIO);
}
if (ldcp->link_state == LDC_CHANNEL_DOWN ||
ldcp->link_state == LDC_CHANNEL_RESET) {
DWARN(ldcp->id,
"ldc_write: (0x%llx) channel down/reset\n", ldcp->id);
*sizep = 0;
if (mutex_tryenter(&ldcp->lock)) {
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->lock);
} else {
mutex_exit(&ldcp->tx_lock);
mutex_enter(&ldcp->lock);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->lock);
}
return (ECONNRESET);
}
tx_tail = ldcp->tx_tail;
tx_head = ldcp->tx_head;
new_tail = (tx_tail + LDC_PACKET_SIZE) &
((ldcp->tx_q_entries-1) << LDC_PACKET_SHIFT);
if (new_tail == tx_head) {
DWARN(DBG_ALL_LDCS,
"ldc_write: (0x%llx) TX queue is full\n", ldcp->id);
*sizep = 0;
return (EWOULDBLOCK);
}
D2(ldcp->id, "ldc_write: (0x%llx) start xfer size=%d",
ldcp->id, size);
ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
bcopy((uint8_t *)buf, ldcmsg, size);
tx_tail = new_tail;
rv = i_ldc_set_tx_tail(ldcp, tx_tail);
if (rv) {
if (rv == EWOULDBLOCK) {
DWARN(ldcp->id, "ldc_write: (0x%llx) write timed out\n",
ldcp->id);
*sizep = 0;
return (EWOULDBLOCK);
}
*sizep = 0;
if (mutex_tryenter(&ldcp->lock)) {
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->lock);
} else {
mutex_exit(&ldcp->tx_lock);
mutex_enter(&ldcp->lock);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->lock);
}
return (ECONNRESET);
}
ldcp->tx_tail = tx_tail;
*sizep = size;
D2(ldcp->id, "ldc_write: (0x%llx) end xfer size=%d", ldcp->id, size);
return (rv);
}
static int
i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t buf, size_t *size)
{
ldc_msg_t *ldcmsg;
uint64_t tx_head, tx_tail, new_tail, start;
uint64_t txq_size_mask, numavail;
uint8_t *msgbuf, *source = (uint8_t *)buf;
size_t len, bytes_written = 0, remaining;
int rv;
uint32_t curr_seqid;
ASSERT(MUTEX_HELD(&ldcp->tx_lock));
ASSERT(ldcp->mode == LDC_MODE_RELIABLE ||
ldcp->mode == LDC_MODE_UNRELIABLE);
txq_size_mask = (ldcp->tx_q_entries - 1) << LDC_PACKET_SHIFT;
rv = hv_ldc_tx_get_state(ldcp->id,
&ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
if (rv != 0) {
cmn_err(CE_WARN,
"ldc_write: (0x%lx) cannot read queue ptrs\n", ldcp->id);
*size = 0;
return (EIO);
}
if (ldcp->link_state == LDC_CHANNEL_DOWN ||
ldcp->link_state == LDC_CHANNEL_RESET) {
DWARN(ldcp->id,
"ldc_write: (0x%llx) channel down/reset\n", ldcp->id);
*size = 0;
if (mutex_tryenter(&ldcp->lock)) {
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->lock);
} else {
mutex_exit(&ldcp->tx_lock);
mutex_enter(&ldcp->lock);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->lock);
}
return (ECONNRESET);
}
tx_tail = ldcp->tx_tail;
new_tail = (tx_tail + LDC_PACKET_SIZE) %
(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
i_ldc_get_tx_head(ldcp, &tx_head);
if (new_tail == tx_head) {
DWARN(DBG_ALL_LDCS,
"ldc_write: (0x%llx) TX queue is full\n", ldcp->id);
*size = 0;
return (EWOULDBLOCK);
}
numavail = (tx_head >> LDC_PACKET_SHIFT) - (tx_tail >> LDC_PACKET_SHIFT)
+ ldcp->tx_q_entries - 1;
numavail %= ldcp->tx_q_entries;
if (*size > (numavail * ldcp->pkt_payload)) {
DWARN(DBG_ALL_LDCS,
"ldc_write: (0x%llx) TX queue has no space\n", ldcp->id);
return (EWOULDBLOCK);
}
D2(ldcp->id, "ldc_write: (0x%llx) start xfer size=%d",
ldcp->id, *size);
bytes_written = 0;
curr_seqid = ldcp->last_msg_snt;
start = tx_tail;
while (*size > bytes_written) {
ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
msgbuf = (uint8_t *)((ldcp->mode == LDC_MODE_RELIABLE) ?
ldcmsg->rdata : ldcmsg->udata);
ldcmsg->type = LDC_DATA;
ldcmsg->stype = LDC_INFO;
ldcmsg->ctrl = 0;
remaining = *size - bytes_written;
len = min(ldcp->pkt_payload, remaining);
ldcmsg->env = (uint8_t)len;
curr_seqid++;
ldcmsg->seqid = curr_seqid;
bcopy(source, msgbuf, len);
source += len;
bytes_written += len;
tx_tail = (tx_tail + LDC_PACKET_SIZE) & txq_size_mask;
ASSERT(tx_tail != tx_head);
}
ldcmsg->env |= LDC_FRAG_STOP;
ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + start);
ldcmsg->env |= LDC_FRAG_START;
rv = i_ldc_set_tx_tail(ldcp, tx_tail);
if (rv == 0) {
ldcp->tx_tail = tx_tail;
ldcp->last_msg_snt = curr_seqid;
*size = bytes_written;
} else {
int rv2;
if (rv != EWOULDBLOCK) {
*size = 0;
if (mutex_tryenter(&ldcp->lock)) {
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->lock);
} else {
mutex_exit(&ldcp->tx_lock);
mutex_enter(&ldcp->lock);
mutex_enter(&ldcp->tx_lock);
i_ldc_reset(ldcp, B_FALSE);
mutex_exit(&ldcp->lock);
}
return (ECONNRESET);
}
D1(ldcp->id, "hv_tx_set_tail returns 0x%x (head 0x%x, "
"old tail 0x%x, new tail 0x%x, qsize=0x%x)\n",
rv, ldcp->tx_head, ldcp->tx_tail, tx_tail,
(ldcp->tx_q_entries << LDC_PACKET_SHIFT));
rv2 = hv_ldc_tx_get_state(ldcp->id,
&tx_head, &tx_tail, &ldcp->link_state);
D1(ldcp->id, "hv_ldc_tx_get_state returns 0x%x "
"(head 0x%x, tail 0x%x state 0x%x)\n",
rv2, tx_head, tx_tail, ldcp->link_state);
*size = 0;
}
D2(ldcp->id, "ldc_write: (0x%llx) end xfer size=%d", ldcp->id, *size);
return (rv);
}
static int
i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep)
{
ASSERT(MUTEX_HELD(&ldcp->tx_lock));
ASSERT(ldcp->mode == LDC_MODE_RELIABLE);
if (*sizep > ldcp->mtu) *sizep = ldcp->mtu;
return (i_ldc_write_packet(ldcp, buf, sizep));
}
int
ldc_register(ldc_cnex_t *cinfo)
{
ldc_chan_t *ldcp;
if (cinfo == NULL || cinfo->dip == NULL ||
cinfo->reg_chan == NULL || cinfo->unreg_chan == NULL ||
cinfo->add_intr == NULL || cinfo->rem_intr == NULL ||
cinfo->clr_intr == NULL) {
DWARN(DBG_ALL_LDCS, "ldc_register: invalid nexus info\n");
return (EINVAL);
}
mutex_enter(&ldcssp->lock);
ldcssp->cinfo.dip = cinfo->dip;
ldcssp->cinfo.reg_chan = cinfo->reg_chan;
ldcssp->cinfo.unreg_chan = cinfo->unreg_chan;
ldcssp->cinfo.add_intr = cinfo->add_intr;
ldcssp->cinfo.rem_intr = cinfo->rem_intr;
ldcssp->cinfo.clr_intr = cinfo->clr_intr;
ldcp = ldcssp->chan_list;
while (ldcp) {
if ((ldcp->tstate & TS_QCONF_RDY) &&
(ldcp->tstate & TS_CNEX_RDY) == 0)
(void) i_ldc_register_channel(ldcp);
ldcp = ldcp->next;
}
mutex_exit(&ldcssp->lock);
return (0);
}
int
ldc_unregister(ldc_cnex_t *cinfo)
{
if (cinfo == NULL || cinfo->dip == NULL) {
DWARN(DBG_ALL_LDCS, "ldc_unregister: invalid nexus info\n");
return (EINVAL);
}
mutex_enter(&ldcssp->lock);
if (cinfo->dip != ldcssp->cinfo.dip) {
DWARN(DBG_ALL_LDCS, "ldc_unregister: invalid dip\n");
mutex_exit(&ldcssp->lock);
return (EINVAL);
}
ldcssp->cinfo.dip = NULL;
ldcssp->cinfo.reg_chan = NULL;
ldcssp->cinfo.unreg_chan = NULL;
ldcssp->cinfo.add_intr = NULL;
ldcssp->cinfo.rem_intr = NULL;
ldcssp->cinfo.clr_intr = NULL;
mutex_exit(&ldcssp->lock);
return (0);
}
int
ldc_info(ldc_handle_t handle, ldc_info_t *info)
{
ldc_chan_t *ldcp;
uint64_t avail;
if (handle == 0 || info == NULL) {
DWARN(DBG_ALL_LDCS, "ldc_get_info: invalid args\n");
return (EINVAL);
}
ldcp = (ldc_chan_t *)handle;
mutex_enter(&ldcp->lock);
if ((ldcp->tstate & ~TS_IN_RESET) < TS_INIT) {
DWARN(ldcp->id,
"ldc_get_info: (0x%llx) channel not initialized\n",
ldcp->id);
mutex_exit(&ldcp->lock);
return (EINVAL);
}
mutex_exit(&ldcp->lock);
if (ldcssp->mapin_size <= ldc_dring_direct_map_rsvd) {
info->direct_map_size_max = 0;
return (0);
}
avail = ldcssp->mapin_size - ldc_dring_direct_map_rsvd;
if (avail >= ldc_direct_map_size_max) {
info->direct_map_size_max = ldc_direct_map_size_max;
} else {
info->direct_map_size_max = 0;
}
return (0);
}