#include <sys/types.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include <sys/time.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strlog.h>
#include <sys/strsubr.h>
#include <sys/cmn_err.h>
#include <sys/cpu.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ksynch.h>
#include <sys/stat.h>
#include <sys/kstat.h>
#include <sys/vtrace.h>
#include <sys/strsun.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <net/if.h>
#include <sys/varargs.h>
#include <sys/machsystm.h>
#include <sys/modctl.h>
#include <sys/modhash.h>
#include <sys/mac.h>
#include <sys/mac_ether.h>
#include <sys/taskq.h>
#include <sys/note.h>
#include <sys/mach_descrip.h>
#include <sys/mdeg.h>
#include <sys/ldc.h>
#include <sys/vsw_fdb.h>
#include <sys/vsw.h>
#include <sys/vio_mailbox.h>
#include <sys/vnet_mailbox.h>
#include <sys/vnet_common.h>
#include <sys/vio_util.h>
#include <sys/sdt.h>
#include <sys/atomic.h>
#include <sys/callb.h>
#include <sys/vlan.h>
static void vsw_port_delete(vsw_port_t *port);
static int vsw_ldc_attach(vsw_port_t *port, uint64_t ldc_id);
static void vsw_ldc_detach(vsw_ldc_t *ldcp);
static int vsw_ldc_init(vsw_ldc_t *ldcp);
static void vsw_ldc_uninit(vsw_ldc_t *ldcp);
static void vsw_ldc_drain(vsw_ldc_t *ldcp);
static void vsw_drain_port_taskq(vsw_port_t *port);
static void vsw_marker_task(void *);
static int vsw_plist_del_node(vsw_t *, vsw_port_t *port);
void vsw_detach_ports(vsw_t *vswp);
int vsw_port_add(vsw_t *vswp, md_t *mdp, mde_cookie_t *node);
mcst_addr_t *vsw_del_addr(uint8_t devtype, void *arg, uint64_t addr);
int vsw_port_detach(vsw_t *vswp, int p_instance);
int vsw_portsend(vsw_port_t *port, mblk_t *mp);
int vsw_port_attach(vsw_port_t *portp);
vsw_port_t *vsw_lookup_port(vsw_t *vswp, int p_instance);
void vsw_vlan_unaware_port_reset(vsw_port_t *portp);
void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
void vsw_reset_ports(vsw_t *vswp);
void vsw_port_reset(vsw_port_t *portp);
void vsw_physlink_update_ports(vsw_t *vswp);
static void vsw_port_physlink_update(vsw_port_t *portp);
static uint_t vsw_ldc_cb(uint64_t cb, caddr_t arg);
static void vsw_ldc_reinit(vsw_ldc_t *);
static void vsw_conn_task(void *);
static int vsw_check_flag(vsw_ldc_t *, int, uint64_t);
static void vsw_next_milestone(vsw_ldc_t *);
static int vsw_supported_version(vio_ver_msg_t *);
static void vsw_set_vnet_proto_ops(vsw_ldc_t *ldcp);
static void vsw_reset_vnet_proto_ops(vsw_ldc_t *ldcp);
void vsw_process_conn_evt(vsw_ldc_t *, uint16_t);
void vsw_process_pkt(void *);
static void vsw_dispatch_ctrl_task(vsw_ldc_t *, void *, vio_msg_tag_t *, int);
static void vsw_process_ctrl_pkt(void *);
static void vsw_process_ctrl_ver_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_attr_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_mcst_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_rdx_pkt(vsw_ldc_t *, void *);
static void vsw_process_physlink_msg(vsw_ldc_t *, void *);
static void vsw_process_data_pkt(vsw_ldc_t *, void *, vio_msg_tag_t *,
uint32_t);
static void vsw_process_pkt_data_nop(void *, void *, uint32_t);
static void vsw_process_pkt_data(void *, void *, uint32_t);
static void vsw_process_data_ibnd_pkt(vsw_ldc_t *, void *);
static void vsw_process_err_pkt(vsw_ldc_t *, void *, vio_msg_tag_t *);
static void vsw_process_evt_read(vsw_ldc_t *ldcp);
static void vsw_ldc_rcv(vsw_ldc_t *ldcp);
static int vsw_descrsend(vsw_ldc_t *, mblk_t *);
static void vsw_ldcsend_pkt(vsw_ldc_t *ldcp, mblk_t *mp);
static int vsw_ldcsend(vsw_ldc_t *ldcp, mblk_t *mp, uint32_t retries);
static int vsw_ldctx_pri(void *arg, mblk_t *mp, mblk_t *mpt, uint32_t count);
static int vsw_ldctx(void *arg, mblk_t *mp, mblk_t *mpt, uint32_t count);
static void vsw_send_ver(void *);
static void vsw_send_attr(vsw_ldc_t *);
static void vsw_send_dring_info(vsw_ldc_t *);
static void vsw_send_rdx(vsw_ldc_t *);
static void vsw_send_physlink_msg(vsw_ldc_t *ldcp, link_state_t plink_state);
static void vsw_create_privring(vsw_ldc_t *);
static dring_info_t *vsw_map_dring(vsw_ldc_t *ldcp, void *pkt);
static void vsw_unmap_dring(vsw_ldc_t *ldcp);
static void vsw_destroy_dring(vsw_ldc_t *ldcp);
static void vsw_free_lane_resources(vsw_ldc_t *, uint64_t);
static int vsw_map_data(vsw_ldc_t *ldcp, dring_info_t *dp, void *pkt);
static void vsw_set_lane_attr(vsw_t *, lane_t *);
dring_info_t *vsw_map_dring_cmn(vsw_ldc_t *ldcp,
vio_dring_reg_msg_t *dring_pkt);
static int vsw_mapin_avail(vsw_ldc_t *ldcp);
static void vsw_stop_tx_thread(vsw_ldc_t *ldcp);
static void vsw_ldc_tx_worker(void *arg);
static void vsw_save_lmacaddr(vsw_t *vswp, uint64_t macaddr);
static int vsw_get_same_dest_list(struct ether_header *ehp,
mblk_t **rhead, mblk_t **rtail, mblk_t **mpp);
static mblk_t *vsw_dupmsgchain(mblk_t *mp);
static void dump_flags(uint64_t);
static void display_state(void);
static void display_lane(lane_t *);
static void display_ring(dring_info_t *);
extern int vsw_set_hw(vsw_t *, vsw_port_t *, int);
extern void vsw_unset_hw(vsw_t *, vsw_port_t *, int);
extern int vsw_add_rem_mcst(vnet_mcast_msg_t *mcst_pkt, vsw_port_t *port);
extern void vsw_del_mcst_port(vsw_port_t *port);
extern int vsw_add_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg);
extern int vsw_del_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg);
extern void vsw_fdbe_add(vsw_t *vswp, void *port);
extern void vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr);
extern void vsw_create_vlans(void *arg, int type);
extern void vsw_destroy_vlans(void *arg, int type);
extern void vsw_vlan_add_ids(void *arg, int type);
extern void vsw_vlan_remove_ids(void *arg, int type);
extern boolean_t vsw_frame_lookup_vid(void *arg, int caller,
struct ether_header *ehp, uint16_t *vidp);
extern mblk_t *vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp);
extern uint32_t vsw_vlan_frame_untag(void *arg, int type, mblk_t **np,
mblk_t **npt);
extern boolean_t vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid);
extern void vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp);
extern void vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp);
extern void vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg);
extern void vsw_hio_stop_port(vsw_port_t *portp);
extern void vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp);
extern int vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type);
extern void vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type);
extern void vsw_destroy_rxpools(void *arg);
extern void vsw_stop_msg_thread(vsw_ldc_t *ldcp);
extern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t);
extern int vsw_dringsend(vsw_ldc_t *, mblk_t *);
extern int vsw_reclaim_dring(dring_info_t *dp, int start);
extern int vsw_dring_find_free_desc(dring_info_t *, vsw_private_desc_t **,
int *);
extern vio_dring_reg_msg_t *vsw_create_tx_dring_info(vsw_ldc_t *);
extern int vsw_setup_tx_dring(vsw_ldc_t *ldcp, dring_info_t *dp);
extern void vsw_destroy_tx_dring(vsw_ldc_t *ldcp);
extern dring_info_t *vsw_map_rx_dring(vsw_ldc_t *ldcp, void *pkt);
extern void vsw_unmap_rx_dring(vsw_ldc_t *ldcp);
extern void vsw_ldc_msg_worker(void *arg);
extern void vsw_process_dringdata(void *, void *);
extern vio_dring_reg_msg_t *vsw_create_rx_dring_info(vsw_ldc_t *);
extern void vsw_destroy_rx_dring(vsw_ldc_t *ldcp);
extern dring_info_t *vsw_map_tx_dring(vsw_ldc_t *ldcp, void *pkt);
extern void vsw_unmap_tx_dring(vsw_ldc_t *ldcp);
extern void vsw_ldc_rcv_worker(void *arg);
extern void vsw_stop_rcv_thread(vsw_ldc_t *ldcp);
extern int vsw_dringsend_shm(vsw_ldc_t *, mblk_t *);
extern void vsw_process_dringdata_shm(void *, void *);
extern int vsw_num_handshakes;
extern int vsw_ldc_tx_delay;
extern int vsw_ldc_tx_retries;
extern int vsw_ldc_retries;
extern int vsw_ldc_delay;
extern boolean_t vsw_ldc_rxthr_enabled;
extern boolean_t vsw_ldc_txthr_enabled;
extern uint32_t vsw_num_descriptors;
extern uint8_t vsw_dring_mode;
extern uint32_t vsw_max_tx_qcount;
extern boolean_t vsw_obp_ver_proto_workaround;
extern uint32_t vsw_publish_macaddr_count;
extern uint32_t vsw_nrbufs_factor;
#define LDC_ENTER_LOCK(ldcp) \
mutex_enter(&((ldcp)->ldc_cblock));\
mutex_enter(&((ldcp)->ldc_rxlock));\
mutex_enter(&((ldcp)->ldc_txlock));
#define LDC_EXIT_LOCK(ldcp) \
mutex_exit(&((ldcp)->ldc_txlock));\
mutex_exit(&((ldcp)->ldc_rxlock));\
mutex_exit(&((ldcp)->ldc_cblock));
#define VSW_VER_EQ(ldcp, major, minor) \
((ldcp)->lane_out.ver_major == (major) && \
(ldcp)->lane_out.ver_minor == (minor))
#define VSW_VER_LT(ldcp, major, minor) \
(((ldcp)->lane_out.ver_major < (major)) || \
((ldcp)->lane_out.ver_major == (major) && \
(ldcp)->lane_out.ver_minor < (minor)))
#define VSW_VER_GTEQ(ldcp, major, minor) \
(((ldcp)->lane_out.ver_major > (major)) || \
((ldcp)->lane_out.ver_major == (major) && \
(ldcp)->lane_out.ver_minor >= (minor)))
#define VSW_VER_LTEQ(ldcp, major, minor) \
(((ldcp)->lane_out.ver_major < (major)) || \
((ldcp)->lane_out.ver_major == (major) && \
(ldcp)->lane_out.ver_minor <= (minor)))
static ver_sup_t vsw_versions[] = { {1, 6} };
#define DUMP_STATE 0
#if DUMP_STATE
#define DUMP_TAG(tag) \
{ \
D1(NULL, "DUMP_TAG: type 0x%llx", (tag).vio_msgtype); \
D1(NULL, "DUMP_TAG: stype 0x%llx", (tag).vio_subtype); \
D1(NULL, "DUMP_TAG: senv 0x%llx", (tag).vio_subtype_env); \
}
#define DUMP_TAG_PTR(tag) \
{ \
D1(NULL, "DUMP_TAG: type 0x%llx", (tag)->vio_msgtype); \
D1(NULL, "DUMP_TAG: stype 0x%llx", (tag)->vio_subtype); \
D1(NULL, "DUMP_TAG: senv 0x%llx", (tag)->vio_subtype_env); \
}
#define DUMP_FLAGS(flags) dump_flags(flags);
#define DISPLAY_STATE() display_state()
#else
#define DUMP_TAG(tag)
#define DUMP_TAG_PTR(tag)
#define DUMP_FLAGS(state)
#define DISPLAY_STATE()
#endif
int
vsw_port_attach(vsw_port_t *port)
{
vsw_t *vswp = port->p_vswp;
vsw_port_list_t *plist = &vswp->plist;
vsw_port_t *p, **pp;
int nids = port->num_ldcs;
uint64_t *ldcids;
int rv;
D1(vswp, "%s: enter : port %d", __func__, port->p_instance);
READ_ENTER(&plist->lockrw);
for (p = plist->head; p != NULL; p = p->p_next) {
if (p->p_instance == port->p_instance) {
DWARN(vswp, "%s: port instance %d already attached",
__func__, p->p_instance);
RW_EXIT(&plist->lockrw);
return (1);
}
}
RW_EXIT(&plist->lockrw);
mutex_init(&port->tx_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&port->mca_lock, NULL, MUTEX_DRIVER, NULL);
rw_init(&port->maccl_rwlock, NULL, RW_DRIVER, NULL);
mutex_init(&port->state_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&port->state_cv, NULL, CV_DRIVER, NULL);
port->state = VSW_PORT_INIT;
D2(vswp, "%s: %d nids", __func__, nids);
ldcids = port->ldc_ids;
D2(vswp, "%s: ldcid (%llx)", __func__, (uint64_t)ldcids[0]);
if (vsw_ldc_attach(port, (uint64_t)ldcids[0]) != 0) {
DERR(vswp, "%s: ldc_attach failed", __func__);
goto exit_error;
}
if (vswp->switching_setup_done == B_TRUE) {
rv = vsw_mac_client_init(vswp, port, VSW_VNETPORT);
if (rv != 0) {
goto exit_error;
}
}
vsw_fdbe_add(vswp, port);
vsw_create_vlans(port, VSW_VNETPORT);
WRITE_ENTER(&plist->lockrw);
pp = (vsw_port_t **)(&plist->head);
port->p_next = *pp;
*pp = port;
plist->num_ports++;
RW_EXIT(&plist->lockrw);
(void) vsw_ldc_init(port->ldcp);
if (vsw_publish_macaddr_count != 0) {
vsw_publish_macaddr(vswp, port);
}
D1(vswp, "%s: exit", __func__);
return (0);
exit_error:
cv_destroy(&port->state_cv);
mutex_destroy(&port->state_lock);
rw_destroy(&port->maccl_rwlock);
mutex_destroy(&port->tx_lock);
mutex_destroy(&port->mca_lock);
kmem_free(port, sizeof (vsw_port_t));
return (1);
}
int
vsw_port_detach(vsw_t *vswp, int p_instance)
{
vsw_port_t *port = NULL;
vsw_port_list_t *plist = &vswp->plist;
D1(vswp, "%s: enter: port id %d", __func__, p_instance);
WRITE_ENTER(&plist->lockrw);
if ((port = vsw_lookup_port(vswp, p_instance)) == NULL) {
RW_EXIT(&plist->lockrw);
return (1);
}
if (vsw_plist_del_node(vswp, port)) {
RW_EXIT(&plist->lockrw);
return (1);
}
vsw_hio_stop_port(port);
RW_EXIT(&plist->lockrw);
vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT);
vsw_fdbe_del(vswp, &(port->p_macaddr));
vsw_destroy_vlans(port, VSW_VNETPORT);
vsw_del_mcst_port(port);
vsw_port_delete(port);
D1(vswp, "%s: exit: p_instance(%d)", __func__, p_instance);
return (0);
}
void
vsw_detach_ports(vsw_t *vswp)
{
vsw_port_list_t *plist = &vswp->plist;
vsw_port_t *port = NULL;
D1(vswp, "%s: enter", __func__);
WRITE_ENTER(&plist->lockrw);
while ((port = plist->head) != NULL) {
(void) vsw_plist_del_node(vswp, port);
vsw_hio_stop_port(port);
vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT);
vsw_fdbe_del(vswp, &(port->p_macaddr));
vsw_destroy_vlans(port, VSW_VNETPORT);
vsw_del_mcst_port(port);
RW_EXIT(&plist->lockrw);
vsw_port_delete(port);
WRITE_ENTER(&plist->lockrw);
}
RW_EXIT(&plist->lockrw);
D1(vswp, "%s: exit", __func__);
}
static void
vsw_port_delete(vsw_port_t *port)
{
vsw_t *vswp = port->p_vswp;
D1(vswp, "%s: enter : port id %d", __func__, port->p_instance);
vsw_ldc_uninit(port->ldcp);
vsw_drain_port_taskq(port);
vsw_ldc_drain(port->ldcp);
vsw_ldc_detach(port->ldcp);
rw_destroy(&port->maccl_rwlock);
mutex_destroy(&port->mca_lock);
mutex_destroy(&port->tx_lock);
cv_destroy(&port->state_cv);
mutex_destroy(&port->state_lock);
if (port->num_ldcs != 0) {
kmem_free(port->ldc_ids, port->num_ldcs * sizeof (uint64_t));
port->num_ldcs = 0;
}
if (port->nvids != 0) {
kmem_free(port->vids, sizeof (vsw_vlanid_t) * port->nvids);
}
kmem_free(port, sizeof (vsw_port_t));
D1(vswp, "%s: exit", __func__);
}
static int
vsw_ldc_attach(vsw_port_t *port, uint64_t ldc_id)
{
vsw_t *vswp = port->p_vswp;
vsw_ldc_t *ldcp = NULL;
ldc_attr_t attr;
ldc_status_t istatus;
int status = DDI_FAILURE;
char kname[MAXNAMELEN];
enum { PROG_init = 0x0,
PROG_callback = 0x1,
PROG_tx_thread = 0x2}
progress;
progress = PROG_init;
D1(vswp, "%s: enter", __func__);
ldcp = kmem_zalloc(sizeof (vsw_ldc_t), KM_NOSLEEP);
if (ldcp == NULL) {
DERR(vswp, "%s: kmem_zalloc failed", __func__);
return (1);
}
ldcp->ldc_id = ldc_id;
mutex_init(&ldcp->ldc_txlock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&ldcp->ldc_rxlock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&ldcp->ldc_cblock, NULL, MUTEX_DRIVER, NULL);
ldcp->msg_thr_flags = 0;
mutex_init(&ldcp->msg_thr_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&ldcp->msg_thr_cv, NULL, CV_DRIVER, NULL);
ldcp->rcv_thr_flags = 0;
mutex_init(&ldcp->rcv_thr_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&ldcp->rcv_thr_cv, NULL, CV_DRIVER, NULL);
mutex_init(&ldcp->drain_cv_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&ldcp->drain_cv, NULL, CV_DRIVER, NULL);
ldcp->local_session = (uint64_t)ddi_get_lbolt();
ldcp->peer_session = 0;
ldcp->session_status = 0;
ldcp->hss_id = 1;
ldcp->hphase = VSW_MILESTONE0;
(void) atomic_swap_32(&port->p_hio_capable, B_FALSE);
vsw_set_lane_attr(vswp, &ldcp->lane_out);
attr.devclass = LDC_DEV_NT_SVC;
attr.instance = ddi_get_instance(vswp->dip);
attr.mode = LDC_MODE_UNRELIABLE;
attr.mtu = VSW_LDC_MTU;
status = ldc_init(ldc_id, &attr, &ldcp->ldc_handle);
if (status != 0) {
DERR(vswp, "%s(%lld): ldc_init failed, rv (%d)",
__func__, ldc_id, status);
goto ldc_attach_fail;
}
if (vsw_ldc_txthr_enabled) {
ldcp->tx_thr_flags = 0;
ldcp->tx_mhead = ldcp->tx_mtail = NULL;
mutex_init(&ldcp->tx_thr_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&ldcp->tx_thr_cv, NULL, CV_DRIVER, NULL);
ldcp->tx_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
vsw_ldc_tx_worker, ldcp, 0, &p0, TS_RUN, maxclsyspri);
progress |= PROG_tx_thread;
if (ldcp->tx_thread == NULL) {
DWARN(vswp, "%s(%lld): Failed to create worker thread",
__func__, ldc_id);
goto ldc_attach_fail;
}
}
status = ldc_reg_callback(ldcp->ldc_handle, vsw_ldc_cb, (caddr_t)ldcp);
if (status != 0) {
DERR(vswp, "%s(%lld): ldc_reg_callback failed, rv (%d)",
__func__, ldc_id, status);
(void) ldc_fini(ldcp->ldc_handle);
goto ldc_attach_fail;
}
ldcp->msglen = VIO_PKT_DATA_HDRSIZE + vswp->max_frame_size;
ldcp->ldcmsg = kmem_alloc(ldcp->msglen, KM_SLEEP);
progress |= PROG_callback;
mutex_init(&ldcp->status_lock, NULL, MUTEX_DRIVER, NULL);
if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
DERR(vswp, "%s: ldc_status failed", __func__);
mutex_destroy(&ldcp->status_lock);
goto ldc_attach_fail;
}
ldcp->ldc_status = istatus;
ldcp->ldc_port = port;
ldcp->ldc_vswp = vswp;
vsw_reset_vnet_proto_ops(ldcp);
(void) sprintf(kname, "%sldc0x%lx", DRV_NAME, ldcp->ldc_id);
ldcp->ksp = vgen_setup_kstats(DRV_NAME, vswp->instance,
kname, &ldcp->ldc_stats);
if (ldcp->ksp == NULL) {
DERR(vswp, "%s: kstats setup failed", __func__);
goto ldc_attach_fail;
}
port->ldcp = ldcp;
D1(vswp, "%s: exit", __func__);
return (0);
ldc_attach_fail:
if (progress & PROG_callback) {
(void) ldc_unreg_callback(ldcp->ldc_handle);
kmem_free(ldcp->ldcmsg, ldcp->msglen);
}
if (progress & PROG_tx_thread) {
if (ldcp->tx_thread != NULL) {
vsw_stop_tx_thread(ldcp);
}
mutex_destroy(&ldcp->tx_thr_lock);
cv_destroy(&ldcp->tx_thr_cv);
}
if (ldcp->ksp != NULL) {
vgen_destroy_kstats(ldcp->ksp);
}
mutex_destroy(&ldcp->msg_thr_lock);
mutex_destroy(&ldcp->rcv_thr_lock);
mutex_destroy(&ldcp->ldc_txlock);
mutex_destroy(&ldcp->ldc_rxlock);
mutex_destroy(&ldcp->ldc_cblock);
mutex_destroy(&ldcp->drain_cv_lock);
cv_destroy(&ldcp->msg_thr_cv);
cv_destroy(&ldcp->rcv_thr_cv);
cv_destroy(&ldcp->drain_cv);
kmem_free(ldcp, sizeof (vsw_ldc_t));
return (1);
}
static void
vsw_ldc_detach(vsw_ldc_t *ldcp)
{
int rv;
vsw_t *vswp = ldcp->ldc_port->p_vswp;
int retries = 0;
D2(vswp, "%s: detaching channel %lld", __func__, ldcp->ldc_id);
if (ldcp->rcv_thread != NULL) {
vsw_stop_rcv_thread(ldcp);
} else if (ldcp->msg_thread != NULL) {
vsw_stop_msg_thread(ldcp);
}
kmem_free(ldcp->ldcmsg, ldcp->msglen);
if (ldcp->tx_thread != NULL) {
vsw_stop_tx_thread(ldcp);
mutex_destroy(&ldcp->tx_thr_lock);
cv_destroy(&ldcp->tx_thr_cv);
if (ldcp->tx_mhead != NULL) {
freemsgchain(ldcp->tx_mhead);
ldcp->tx_mhead = ldcp->tx_mtail = NULL;
ldcp->tx_cnt = 0;
}
}
vgen_destroy_kstats(ldcp->ksp);
vsw_free_lane_resources(ldcp, INBOUND);
vsw_free_lane_resources(ldcp, OUTBOUND);
while ((rv = ldc_close(ldcp->ldc_handle)) == EAGAIN) {
if (++retries > vsw_ldc_retries) {
break;
}
drv_usecwait(vsw_ldc_delay);
}
if (rv != 0) {
cmn_err(CE_NOTE,
"!vsw%d: Error(%d) closing the channel(0x%lx)\n",
vswp->instance, rv, ldcp->ldc_id);
}
(void) ldc_fini(ldcp->ldc_handle);
ldcp->ldc_status = LDC_INIT;
ldcp->ldc_handle = 0;
ldcp->ldc_vswp = NULL;
mutex_destroy(&ldcp->msg_thr_lock);
mutex_destroy(&ldcp->rcv_thr_lock);
mutex_destroy(&ldcp->ldc_txlock);
mutex_destroy(&ldcp->ldc_rxlock);
mutex_destroy(&ldcp->ldc_cblock);
mutex_destroy(&ldcp->drain_cv_lock);
mutex_destroy(&ldcp->status_lock);
cv_destroy(&ldcp->msg_thr_cv);
cv_destroy(&ldcp->rcv_thr_cv);
cv_destroy(&ldcp->drain_cv);
kmem_free(ldcp, sizeof (vsw_ldc_t));
}
static int
vsw_ldc_init(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
ldc_status_t istatus = 0;
int rv;
D1(vswp, "%s: enter", __func__);
LDC_ENTER_LOCK(ldcp);
ldcp->next_ident = 1;
rv = ldc_open(ldcp->ldc_handle);
if (rv != 0) {
DERR(vswp, "%s: ldc_open failed: id(%lld) rv(%d)",
__func__, ldcp->ldc_id, rv);
LDC_EXIT_LOCK(ldcp);
return (1);
}
if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
DERR(vswp, "%s: unable to get status", __func__);
LDC_EXIT_LOCK(ldcp);
return (1);
} else if (istatus != LDC_OPEN && istatus != LDC_READY) {
DERR(vswp, "%s: id (%lld) status(%d) is not OPEN/READY",
__func__, ldcp->ldc_id, istatus);
LDC_EXIT_LOCK(ldcp);
return (1);
}
mutex_enter(&ldcp->status_lock);
ldcp->ldc_status = istatus;
mutex_exit(&ldcp->status_lock);
rv = ldc_up(ldcp->ldc_handle);
if (rv != 0) {
D2(vswp, "%s: ldc_up err id(%lld) rv(%d)", __func__,
ldcp->ldc_id, rv);
LDC_EXIT_LOCK(ldcp);
return (1);
}
mutex_enter(&ldcp->status_lock);
if (ldc_status(ldcp->ldc_handle, &ldcp->ldc_status) != 0) {
DERR(vswp, "%s: unable to get status", __func__);
mutex_exit(&ldcp->status_lock);
LDC_EXIT_LOCK(ldcp);
return (1);
}
if (ldcp->ldc_status == LDC_UP) {
D2(vswp, "%s: channel %ld now UP (%ld)", __func__,
ldcp->ldc_id, istatus);
mutex_exit(&ldcp->status_lock);
LDC_EXIT_LOCK(ldcp);
vsw_process_conn_evt(ldcp, VSW_CONN_UP);
return (0);
}
mutex_exit(&ldcp->status_lock);
LDC_EXIT_LOCK(ldcp);
D1(vswp, "%s: exit", __func__);
return (0);
}
static void
vsw_ldc_uninit(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
int rv;
D1(vswp, "vsw_ldc_uninit: enter: id(%lx)\n", ldcp->ldc_id);
LDC_ENTER_LOCK(ldcp);
rv = ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_DISABLE);
if (rv != 0) {
cmn_err(CE_NOTE, "!vsw_ldc_uninit(%ld): error disabling "
"interrupts (rv = %d)\n", ldcp->ldc_id, rv);
}
mutex_enter(&ldcp->status_lock);
ldcp->ldc_status = LDC_INIT;
mutex_exit(&ldcp->status_lock);
LDC_EXIT_LOCK(ldcp);
D1(vswp, "vsw_ldc_uninit: exit: id(%lx)", ldcp->ldc_id);
}
static void
vsw_ldc_drain(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_port->p_vswp;
D1(vswp, "%s: enter", __func__);
mutex_enter(&ldcp->drain_cv_lock);
ldcp->drain_state = VSW_LDC_DRAINING;
if ((ldc_unreg_callback(ldcp->ldc_handle)) == 0) {
D2(vswp, "%s: unreg callback for chan %ld", __func__,
ldcp->ldc_id);
mutex_exit(&ldcp->drain_cv_lock);
} else {
while (ldc_unreg_callback(ldcp->ldc_handle) == EWOULDBLOCK) {
(void) cv_timedwait(&ldcp->drain_cv,
&ldcp->drain_cv_lock, ddi_get_lbolt() + hz);
}
mutex_exit(&ldcp->drain_cv_lock);
D2(vswp, "%s: unreg callback for chan %ld after "
"timeout", __func__, ldcp->ldc_id);
}
D1(vswp, "%s: exit", __func__);
}
static void
vsw_drain_port_taskq(vsw_port_t *port)
{
vsw_t *vswp = port->p_vswp;
D1(vswp, "%s: enter", __func__);
mutex_enter(&port->state_lock);
port->state = VSW_PORT_DETACHING;
if ((vswp->taskq_p == NULL) ||
(ddi_taskq_dispatch(vswp->taskq_p, vsw_marker_task,
port, DDI_NOSLEEP) != DDI_SUCCESS)) {
cmn_err(CE_NOTE, "!vsw%d: unable to dispatch marker task",
vswp->instance);
mutex_exit(&port->state_lock);
return;
}
while (port->state != VSW_PORT_DETACHABLE)
cv_wait(&port->state_cv, &port->state_lock);
mutex_exit(&port->state_lock);
D1(vswp, "%s: exit", __func__);
}
static void
vsw_marker_task(void *arg)
{
vsw_port_t *port = arg;
vsw_t *vswp = port->p_vswp;
D1(vswp, "%s: enter", __func__);
mutex_enter(&port->state_lock);
port->state = VSW_PORT_DETACHABLE;
cv_signal(&port->state_cv);
mutex_exit(&port->state_lock);
D1(vswp, "%s: exit", __func__);
}
vsw_port_t *
vsw_lookup_port(vsw_t *vswp, int p_instance)
{
vsw_port_list_t *plist = &vswp->plist;
vsw_port_t *port;
for (port = plist->head; port != NULL; port = port->p_next) {
if (port->p_instance == p_instance) {
D2(vswp, "vsw_lookup_port: found p_instance\n");
return (port);
}
}
return (NULL);
}
void
vsw_vlan_unaware_port_reset(vsw_port_t *portp)
{
vsw_ldc_t *ldcp = portp->ldcp;
mutex_enter(&ldcp->ldc_cblock);
if (ldcp->hphase == VSW_MILESTONE4 && VSW_VER_LT(ldcp, 1, 3) &&
portp->nvids != 0) {
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
}
mutex_exit(&ldcp->ldc_cblock);
}
void
vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate)
{
vsw_ldc_t *ldcp = portp->ldcp;
mutex_enter(&ldcp->ldc_cblock);
if ((ldcp->hphase == VSW_MILESTONE4) &&
(portp->p_hio_capable == B_TRUE)) {
if (immediate == B_TRUE) {
(void) ldc_down(ldcp->ldc_handle);
} else {
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
}
}
mutex_exit(&ldcp->ldc_cblock);
}
void
vsw_port_reset(vsw_port_t *portp)
{
vsw_ldc_t *ldcp = portp->ldcp;
mutex_enter(&ldcp->ldc_cblock);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
mutex_exit(&ldcp->ldc_cblock);
}
void
vsw_reset_ports(vsw_t *vswp)
{
vsw_port_list_t *plist = &vswp->plist;
vsw_port_t *portp;
READ_ENTER(&plist->lockrw);
for (portp = plist->head; portp != NULL; portp = portp->p_next) {
if ((portp->p_hio_capable) && (portp->p_hio_enabled)) {
vsw_hio_stop_port(portp);
}
vsw_port_reset(portp);
}
RW_EXIT(&plist->lockrw);
}
static void
vsw_send_physlink_msg(vsw_ldc_t *ldcp, link_state_t plink_state)
{
vnet_physlink_msg_t msg;
vnet_physlink_msg_t *msgp = &msg;
uint32_t physlink_info = 0;
if (plink_state == LINK_STATE_UP) {
physlink_info |= VNET_PHYSLINK_STATE_UP;
} else {
physlink_info |= VNET_PHYSLINK_STATE_DOWN;
}
msgp->tag.vio_msgtype = VIO_TYPE_CTRL;
msgp->tag.vio_subtype = VIO_SUBTYPE_INFO;
msgp->tag.vio_subtype_env = VNET_PHYSLINK_INFO;
msgp->tag.vio_sid = ldcp->local_session;
msgp->physlink_info = physlink_info;
(void) vsw_send_msg(ldcp, msgp, sizeof (msg), B_TRUE);
}
static void
vsw_port_physlink_update(vsw_port_t *portp)
{
vsw_ldc_t *ldcp;
vsw_t *vswp;
vswp = portp->p_vswp;
ldcp = portp->ldcp;
mutex_enter(&ldcp->ldc_cblock);
if (ldcp->hphase == VSW_MILESTONE4 && ldcp->pls_negotiated == B_TRUE) {
vsw_send_physlink_msg(ldcp, vswp->phys_link_state);
}
mutex_exit(&ldcp->ldc_cblock);
}
void
vsw_physlink_update_ports(vsw_t *vswp)
{
vsw_port_list_t *plist = &vswp->plist;
vsw_port_t *portp;
READ_ENTER(&plist->lockrw);
for (portp = plist->head; portp != NULL; portp = portp->p_next) {
vsw_port_physlink_update(portp);
}
RW_EXIT(&plist->lockrw);
}
static int
vsw_plist_del_node(vsw_t *vswp, vsw_port_t *port)
{
vsw_port_list_t *plist = &vswp->plist;
vsw_port_t *curr_p, *prev_p;
if (plist->head == NULL)
return (1);
curr_p = prev_p = plist->head;
while (curr_p != NULL) {
if (curr_p == port) {
if (prev_p == curr_p) {
plist->head = curr_p->p_next;
} else {
prev_p->p_next = curr_p->p_next;
}
plist->num_ports--;
break;
} else {
prev_p = curr_p;
curr_p = curr_p->p_next;
}
}
return (0);
}
static uint_t
vsw_ldc_cb(uint64_t event, caddr_t arg)
{
vsw_ldc_t *ldcp = (vsw_ldc_t *)arg;
vsw_t *vswp = ldcp->ldc_vswp;
D1(vswp, "%s: enter: ldcid (%lld)\n", __func__, ldcp->ldc_id);
mutex_enter(&ldcp->ldc_cblock);
ldcp->ldc_stats.callbacks++;
mutex_enter(&ldcp->status_lock);
if ((ldcp->ldc_status == LDC_INIT) || (ldcp->ldc_handle == 0)) {
mutex_exit(&ldcp->status_lock);
mutex_exit(&ldcp->ldc_cblock);
return (LDC_SUCCESS);
}
mutex_exit(&ldcp->status_lock);
if (event & LDC_EVT_UP) {
D2(vswp, "%s: id(%ld) event(%llx) UP: status(%ld)",
__func__, ldcp->ldc_id, event, ldcp->ldc_status);
vsw_process_conn_evt(ldcp, VSW_CONN_UP);
ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0);
}
if (event & LDC_EVT_READ) {
D2(vswp, "%s: id(ld) event(%llx) data READ",
__func__, ldcp->ldc_id, event);
vsw_process_evt_read(ldcp);
ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0);
goto vsw_cb_exit;
}
if (event & (LDC_EVT_DOWN | LDC_EVT_RESET)) {
D2(vswp, "%s: id(%ld) event (%lx) DOWN/RESET: status(%ld)",
__func__, ldcp->ldc_id, event, ldcp->ldc_status);
vsw_process_conn_evt(ldcp, VSW_CONN_RESET);
}
if (event &
~(LDC_EVT_UP | LDC_EVT_RESET | LDC_EVT_DOWN | LDC_EVT_READ)) {
DERR(vswp, "%s: id(%ld) Unexpected event=(%llx) status(%ld)",
__func__, ldcp->ldc_id, event, ldcp->ldc_status);
}
vsw_cb_exit:
mutex_exit(&ldcp->ldc_cblock);
mutex_enter(&ldcp->drain_cv_lock);
if (ldcp->drain_state == VSW_LDC_DRAINING)
cv_signal(&ldcp->drain_cv);
mutex_exit(&ldcp->drain_cv_lock);
return (LDC_SUCCESS);
}
static void
vsw_ldc_reinit(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
vsw_port_t *port;
D1(vswp, "%s: enter", __func__);
port = ldcp->ldc_port;
D2(vswp, "%s: in 0x%llx : out 0x%llx", __func__,
ldcp->lane_in.lstate, ldcp->lane_out.lstate);
vsw_free_lane_resources(ldcp, INBOUND);
vsw_free_lane_resources(ldcp, OUTBOUND);
ldcp->lane_in.lstate = 0;
ldcp->lane_out.lstate = 0;
vsw_del_mcst_port(port);
ldcp->peer_session = 0;
ldcp->session_status = 0;
ldcp->hcnt = 0;
ldcp->hphase = VSW_MILESTONE0;
vsw_reset_vnet_proto_ops(ldcp);
D1(vswp, "%s: exit", __func__);
}
void
vsw_process_conn_evt(vsw_ldc_t *ldcp, uint16_t evt)
{
vsw_t *vswp = ldcp->ldc_vswp;
vsw_conn_evt_t *conn = NULL;
D1(vswp, "%s: enter", __func__);
if (((evt == VSW_CONN_RESET) || (evt == VSW_CONN_RESTART)) &&
(ldstub((uint8_t *)&ldcp->reset_active)))
return;
mutex_enter(&ldcp->status_lock);
if (evt == VSW_CONN_UP) {
if ((ldcp->ldc_status == LDC_UP) || (ldcp->reset_active != 0)) {
mutex_exit(&ldcp->status_lock);
return;
}
}
mutex_exit(&ldcp->status_lock);
(void) atomic_inc_32(&ldcp->hss_id);
ASSERT(vswp->taskq_p != NULL);
if ((conn = kmem_zalloc(sizeof (vsw_conn_evt_t), KM_NOSLEEP)) == NULL) {
cmn_err(CE_WARN, "!vsw%d: unable to allocate memory for"
" connection event", vswp->instance);
goto err_exit;
}
conn->evt = evt;
conn->ldcp = ldcp;
if (ddi_taskq_dispatch(vswp->taskq_p, vsw_conn_task, conn,
DDI_NOSLEEP) != DDI_SUCCESS) {
cmn_err(CE_WARN, "!vsw%d: Can't dispatch connection task",
vswp->instance);
kmem_free(conn, sizeof (vsw_conn_evt_t));
goto err_exit;
}
D1(vswp, "%s: exit", __func__);
return;
err_exit:
if ((evt == VSW_CONN_RESET) || (evt == VSW_CONN_RESTART))
ldcp->reset_active = 0;
}
static void
vsw_conn_task(void *arg)
{
vsw_conn_evt_t *conn = (vsw_conn_evt_t *)arg;
vsw_ldc_t *ldcp = NULL;
vsw_port_t *portp;
vsw_t *vswp = NULL;
uint16_t evt;
ldc_status_t curr_status;
ldcp = conn->ldcp;
evt = conn->evt;
vswp = ldcp->ldc_vswp;
portp = ldcp->ldc_port;
D1(vswp, "%s: enter", __func__);
kmem_free(conn, sizeof (vsw_conn_evt_t));
if (ldcp->rcv_thread != NULL) {
vsw_stop_rcv_thread(ldcp);
} else if (ldcp->msg_thread != NULL) {
vsw_stop_msg_thread(ldcp);
}
mutex_enter(&ldcp->status_lock);
if (ldc_status(ldcp->ldc_handle, &curr_status) != 0) {
cmn_err(CE_WARN, "!vsw%d: Unable to read status of "
"channel %ld", vswp->instance, ldcp->ldc_id);
mutex_exit(&ldcp->status_lock);
return;
}
if ((evt == VSW_CONN_RESTART) && (curr_status == LDC_UP))
(void) ldc_down(ldcp->ldc_handle);
if ((portp->p_hio_capable) && (portp->p_hio_enabled)) {
vsw_hio_stop(vswp, ldcp);
}
vsw_ldc_reinit(ldcp);
(void) ldc_up(ldcp->ldc_handle);
if (ldc_status(ldcp->ldc_handle, &curr_status) != 0) {
cmn_err(CE_WARN, "!vsw%d: Unable to read status of "
"channel %ld", vswp->instance, ldcp->ldc_id);
mutex_exit(&ldcp->status_lock);
return;
}
ldcp->ldc_status = curr_status;
if (curr_status == LDC_UP) {
if (ldcp->hcnt++ > vsw_num_handshakes) {
cmn_err(CE_WARN, "!vsw%d: exceeded number of permitted"
" handshake attempts (%d) on channel %ld",
vswp->instance, ldcp->hcnt, ldcp->ldc_id);
mutex_exit(&ldcp->status_lock);
return;
}
if (vsw_obp_ver_proto_workaround == B_FALSE &&
(ddi_taskq_dispatch(vswp->taskq_p, vsw_send_ver, ldcp,
DDI_NOSLEEP) != DDI_SUCCESS)) {
cmn_err(CE_WARN, "!vsw%d: Can't dispatch version task",
vswp->instance);
if (ldcp->hcnt > 0)
ldcp->hcnt--;
}
}
if ((evt == VSW_CONN_RESET) || (evt == VSW_CONN_RESTART))
ldcp->reset_active = 0;
mutex_exit(&ldcp->status_lock);
D1(vswp, "%s: exit", __func__);
}
int
vsw_check_flag(vsw_ldc_t *ldcp, int dir, uint64_t flag)
{
vsw_t *vswp = ldcp->ldc_vswp;
uint64_t state;
uint64_t phase;
if (dir == INBOUND)
state = ldcp->lane_in.lstate;
else
state = ldcp->lane_out.lstate;
phase = ldcp->hphase;
switch (flag) {
case VSW_VER_INFO_RECV:
if (phase > VSW_MILESTONE0) {
DERR(vswp, "vsw_check_flag (%d): VER_INFO_RECV"
" when in state %d\n", ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
}
break;
case VSW_VER_ACK_RECV:
case VSW_VER_NACK_RECV:
if (!(state & VSW_VER_INFO_SENT)) {
DERR(vswp, "vsw_check_flag (%d): spurious VER_ACK or "
"VER_NACK when in state %d\n", ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
} else
state &= ~VSW_VER_INFO_SENT;
break;
case VSW_ATTR_INFO_RECV:
if ((phase < VSW_MILESTONE1) || (phase >= VSW_MILESTONE2)) {
DERR(vswp, "vsw_check_flag (%d): ATTR_INFO_RECV"
" when in state %d\n", ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
}
break;
case VSW_ATTR_ACK_RECV:
case VSW_ATTR_NACK_RECV:
if (!(state & VSW_ATTR_INFO_SENT)) {
DERR(vswp, "vsw_check_flag (%d): spurious ATTR_ACK"
" or ATTR_NACK when in state %d\n",
ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
} else
state &= ~VSW_ATTR_INFO_SENT;
break;
case VSW_DRING_INFO_RECV:
if (phase < VSW_MILESTONE1) {
DERR(vswp, "vsw_check_flag (%d): DRING_INFO_RECV"
" when in state %d\n", ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
}
break;
case VSW_DRING_ACK_RECV:
case VSW_DRING_NACK_RECV:
if (!(state & VSW_DRING_INFO_SENT)) {
DERR(vswp, "vsw_check_flag (%d): spurious DRING_ACK "
" or DRING_NACK when in state %d\n",
ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
} else
state &= ~VSW_DRING_INFO_SENT;
break;
case VSW_RDX_INFO_RECV:
if (phase < VSW_MILESTONE3) {
DERR(vswp, "vsw_check_flag (%d): RDX_INFO_RECV"
" when in state %d\n", ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
}
break;
case VSW_RDX_ACK_RECV:
case VSW_RDX_NACK_RECV:
if (!(state & VSW_RDX_INFO_SENT)) {
DERR(vswp, "vsw_check_flag (%d): spurious RDX_ACK or "
"RDX_NACK when in state %d\n", ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
} else
state &= ~VSW_RDX_INFO_SENT;
break;
case VSW_MCST_INFO_RECV:
if (phase < VSW_MILESTONE3) {
DERR(vswp, "vsw_check_flag (%d): VSW_MCST_INFO_RECV"
" when in state %d\n", ldcp->ldc_id, phase);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return (1);
}
break;
default:
DERR(vswp, "vsw_check_flag (%lld): unknown flag (%llx)",
ldcp->ldc_id, flag);
return (1);
}
if (dir == INBOUND)
ldcp->lane_in.lstate = state;
else
ldcp->lane_out.lstate = state;
D1(vswp, "vsw_check_flag (chan %lld): exit", ldcp->ldc_id);
return (0);
}
void
vsw_next_milestone(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
vsw_port_t *portp = ldcp->ldc_port;
lane_t *lane_out = &ldcp->lane_out;
lane_t *lane_in = &ldcp->lane_in;
D1(vswp, "%s (chan %lld): enter (phase %ld)", __func__,
ldcp->ldc_id, ldcp->hphase);
DUMP_FLAGS(lane_in->lstate);
DUMP_FLAGS(lane_out->lstate);
switch (ldcp->hphase) {
case VSW_MILESTONE0:
if (lane_out->lstate == 0) {
D2(vswp, "%s: (chan %lld) starting handshake "
"with peer", __func__, ldcp->ldc_id);
vsw_process_conn_evt(ldcp, VSW_CONN_UP);
}
if ((lane_in->lstate & VSW_VER_ACK_SENT) &&
(lane_out->lstate & VSW_VER_ACK_RECV)) {
D2(vswp, "%s: (chan %lld) leaving milestone 0",
__func__, ldcp->ldc_id);
vsw_set_vnet_proto_ops(ldcp);
ldcp->hphase = VSW_MILESTONE1;
vsw_send_attr(ldcp);
}
break;
case VSW_MILESTONE1:
if (!((lane_in->lstate & VSW_ATTR_ACK_SENT) &&
(lane_out->lstate & VSW_ATTR_ACK_RECV))) {
break;
}
ldcp->hphase = VSW_MILESTONE2;
if ((VSW_VER_GTEQ(ldcp, 1, 2) &&
(lane_in->xfer_mode & VIO_DRING_MODE_V1_2)) ||
(VSW_VER_LT(ldcp, 1, 2) &&
(lane_in->xfer_mode == VIO_DRING_MODE_V1_0))) {
vsw_send_dring_info(ldcp);
break;
}
case VSW_MILESTONE2:
if ((VSW_VER_GTEQ(ldcp, 1, 2) &&
(lane_in->xfer_mode & VIO_DRING_MODE_V1_2)) ||
(VSW_VER_LT(ldcp, 1, 2) &&
(lane_in->xfer_mode ==
VIO_DRING_MODE_V1_0))) {
if (!(lane_in->lstate & VSW_DRING_ACK_SENT))
break;
}
D2(vswp, "%s: (chan %lld) leaving milestone 2",
__func__, ldcp->ldc_id);
ldcp->hphase = VSW_MILESTONE3;
vsw_send_rdx(ldcp);
break;
case VSW_MILESTONE3:
if ((lane_out->lstate & VSW_RDX_ACK_SENT) &&
(lane_in->lstate & VSW_RDX_ACK_RECV)) {
D2(vswp, "%s: (chan %lld) leaving milestone 3",
__func__, ldcp->ldc_id);
D2(vswp, "%s: ** handshake complete (0x%llx : "
"0x%llx) **", __func__, lane_in->lstate,
lane_out->lstate);
if (lane_out->dring_mode == VIO_RX_DRING_DATA) {
lane_in->lstate |= VSW_LANE_ACTIVE;
} else {
lane_out->lstate |= VSW_LANE_ACTIVE;
}
ldcp->hphase = VSW_MILESTONE4;
ldcp->hcnt = 0;
DISPLAY_STATE();
if ((portp->p_hio_enabled) && (portp->p_hio_capable)) {
D2(vswp, "%s: start HybridIO setup", __func__);
vsw_hio_start(vswp, ldcp);
}
if (ldcp->pls_negotiated == B_TRUE) {
vsw_send_physlink_msg(ldcp,
vswp->phys_link_state);
}
} else {
D2(vswp, "%s: still in milestone 3 (0x%llx : 0x%llx)",
__func__, lane_in->lstate,
lane_out->lstate);
}
break;
case VSW_MILESTONE4:
D2(vswp, "%s: (chan %lld) in milestone 4", __func__,
ldcp->ldc_id);
break;
default:
DERR(vswp, "%s: (chan %lld) Unknown Phase %x", __func__,
ldcp->ldc_id, ldcp->hphase);
}
D1(vswp, "%s (chan %lld): exit (phase %ld)", __func__, ldcp->ldc_id,
ldcp->hphase);
}
static int
vsw_supported_version(vio_ver_msg_t *vp)
{
int i;
D1(NULL, "vsw_supported_version: enter");
for (i = 0; i < VSW_NUM_VER; i++) {
if (vsw_versions[i].ver_major == vp->ver_major) {
if (vp->ver_minor > vsw_versions[i].ver_minor) {
D2(NULL, "%s: adjusting minor value from %d "
"to %d", __func__, vp->ver_minor,
vsw_versions[i].ver_minor);
vp->ver_minor = vsw_versions[i].ver_minor;
}
return (0);
}
if (vsw_versions[i].ver_major < vp->ver_major) {
D2(NULL, "%s: adjusting major and minor "
"values to %d, %d\n",
__func__, vsw_versions[i].ver_major,
vsw_versions[i].ver_minor);
vp->ver_major = vsw_versions[i].ver_major;
vp->ver_minor = vsw_versions[i].ver_minor;
return (1);
}
}
vp->ver_major = 0;
vp->ver_minor = 0;
D1(NULL, "vsw_supported_version: exit");
return (1);
}
static void
vsw_set_vnet_proto_ops(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lp = &ldcp->lane_out;
if (VSW_VER_GTEQ(ldcp, 1, 6)) {
if (vsw_mapin_avail(ldcp) == B_TRUE) {
lp->dring_mode = (VIO_RX_DRING_DATA | VIO_TX_DRING);
} else {
lp->dring_mode = VIO_TX_DRING;
}
} else {
lp->dring_mode = VIO_TX_DRING;
}
if (VSW_VER_GTEQ(ldcp, 1, 4)) {
lp->mtu = vswp->max_frame_size;
} else if (VSW_VER_EQ(ldcp, 1, 3)) {
lp->mtu = ETHERMAX + VLAN_TAGSZ;
} else {
vsw_port_t *portp = ldcp->ldc_port;
if (portp->nvids == 0) {
lp->mtu = ETHERMAX;
}
}
if (VSW_VER_GTEQ(ldcp, 1, 2)) {
if (VSW_PRI_ETH_DEFINED(vswp)) {
ldcp->tx = vsw_ldctx_pri;
ldcp->rx_pktdata = vsw_process_pkt_data;
lp->xfer_mode = VIO_PKT_MODE | VIO_DRING_MODE_V1_2;
} else {
ldcp->tx = vsw_ldctx;
ldcp->rx_pktdata = vsw_process_pkt_data_nop;
lp->xfer_mode = VIO_DRING_MODE_V1_2;
}
} else {
vsw_reset_vnet_proto_ops(ldcp);
}
}
static void
vsw_reset_vnet_proto_ops(vsw_ldc_t *ldcp)
{
lane_t *lp = &ldcp->lane_out;
ldcp->tx = vsw_ldctx;
ldcp->rx_pktdata = vsw_process_pkt_data_nop;
lp->xfer_mode = VIO_DRING_MODE_V1_0;
}
static void
vsw_process_evt_read(vsw_ldc_t *ldcp)
{
if (ldcp->msg_thread != NULL) {
mutex_exit(&ldcp->ldc_cblock);
mutex_enter(&ldcp->msg_thr_lock);
if (!(ldcp->msg_thr_flags & VSW_WTHR_DATARCVD)) {
ldcp->msg_thr_flags |= VSW_WTHR_DATARCVD;
cv_signal(&ldcp->msg_thr_cv);
}
mutex_exit(&ldcp->msg_thr_lock);
mutex_enter(&ldcp->ldc_cblock);
} else {
vsw_process_pkt(ldcp);
}
}
void
vsw_process_pkt(void *arg)
{
vsw_ldc_t *ldcp = (vsw_ldc_t *)arg;
vsw_t *vswp = ldcp->ldc_vswp;
size_t msglen;
vio_msg_tag_t *tagp;
uint64_t *ldcmsg;
int rv = 0;
D1(vswp, "%s enter: ldcid (%lld)\n", __func__, ldcp->ldc_id);
ASSERT(MUTEX_HELD(&ldcp->ldc_cblock));
ldcmsg = ldcp->ldcmsg;
do {
msglen = ldcp->msglen;
rv = ldc_read(ldcp->ldc_handle, (caddr_t)ldcmsg, &msglen);
if (rv != 0) {
DERR(vswp, "%s :ldc_read err id(%lld) rv(%d) len(%d)\n",
__func__, ldcp->ldc_id, rv, msglen);
}
if (rv == ECONNRESET) {
vsw_process_conn_evt(ldcp, VSW_CONN_RESET);
break;
}
if (msglen == 0) {
D2(vswp, "%s: ldc_read id(%lld) NODATA", __func__,
ldcp->ldc_id);
break;
}
D2(vswp, "%s: ldc_read id(%lld): msglen(%d)", __func__,
ldcp->ldc_id, msglen);
tagp = (vio_msg_tag_t *)ldcmsg;
switch (tagp->vio_msgtype) {
case VIO_TYPE_CTRL:
vsw_dispatch_ctrl_task(ldcp, ldcmsg, tagp, msglen);
break;
case VIO_TYPE_DATA:
vsw_process_data_pkt(ldcp, ldcmsg, tagp, msglen);
break;
case VIO_TYPE_ERR:
vsw_process_err_pkt(ldcp, ldcmsg, tagp);
break;
default:
DERR(vswp, "%s: Unknown tag(%lx) ", __func__,
"id(%lx)\n", tagp->vio_msgtype, ldcp->ldc_id);
break;
}
} while (msglen);
D1(vswp, "%s exit: ldcid (%lld)\n", __func__, ldcp->ldc_id);
}
static void
vsw_dispatch_ctrl_task(vsw_ldc_t *ldcp, void *cpkt, vio_msg_tag_t *tagp,
int msglen)
{
vsw_ctrl_task_t *ctaskp = NULL;
vsw_port_t *port = ldcp->ldc_port;
vsw_t *vswp = port->p_vswp;
D1(vswp, "%s: enter", __func__);
if ((tagp->vio_subtype_env == VIO_RDX) &&
(tagp->vio_subtype == VIO_SUBTYPE_ACK)) {
if (vsw_check_flag(ldcp, INBOUND, VSW_RDX_ACK_RECV))
return;
ldcp->lane_in.lstate |= VSW_RDX_ACK_RECV;
D2(vswp, "%s (%ld) handling RDX_ACK in place "
"(ostate 0x%llx : hphase %d)", __func__,
ldcp->ldc_id, ldcp->lane_in.lstate, ldcp->hphase);
vsw_next_milestone(ldcp);
return;
}
ctaskp = kmem_alloc(sizeof (vsw_ctrl_task_t), KM_NOSLEEP);
if (ctaskp == NULL) {
DERR(vswp, "%s: unable to alloc space for ctrl msg", __func__);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return;
}
ctaskp->ldcp = ldcp;
bcopy((def_msg_t *)cpkt, &ctaskp->pktp, msglen);
ctaskp->hss_id = ldcp->hss_id;
mutex_enter(&port->state_lock);
if (port->state == VSW_PORT_INIT) {
if ((vswp->taskq_p == NULL) ||
(ddi_taskq_dispatch(vswp->taskq_p, vsw_process_ctrl_pkt,
ctaskp, DDI_NOSLEEP) != DDI_SUCCESS)) {
mutex_exit(&port->state_lock);
DERR(vswp, "%s: unable to dispatch task to taskq",
__func__);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
return;
}
} else {
kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
DWARN(vswp, "%s: port %d detaching, not dispatching "
"task", __func__, port->p_instance);
}
mutex_exit(&port->state_lock);
D2(vswp, "%s: dispatched task to taskq for chan %d", __func__,
ldcp->ldc_id);
D1(vswp, "%s: exit", __func__);
}
static void
vsw_process_ctrl_pkt(void *arg)
{
vsw_ctrl_task_t *ctaskp = (vsw_ctrl_task_t *)arg;
vsw_ldc_t *ldcp = ctaskp->ldcp;
vsw_t *vswp = ldcp->ldc_vswp;
vio_msg_tag_t tag;
uint16_t env;
D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
bcopy(&ctaskp->pktp, &tag, sizeof (vio_msg_tag_t));
env = tag.vio_subtype_env;
if (ctaskp->hss_id < ldcp->hss_id) {
DWARN(vswp, "%s: discarding stale packet belonging to earlier"
" (%ld) handshake session", __func__, ctaskp->hss_id);
kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
return;
}
if (ldcp->session_status & VSW_PEER_SESSION) {
if (ldcp->peer_session != tag.vio_sid) {
DERR(vswp, "%s (chan %d): invalid session id (%llx)",
__func__, ldcp->ldc_id, tag.vio_sid);
kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return;
}
}
switch (env) {
case VIO_VER_INFO:
vsw_process_ctrl_ver_pkt(ldcp, &ctaskp->pktp);
break;
case VIO_DRING_REG:
vsw_process_ctrl_dring_reg_pkt(ldcp, &ctaskp->pktp);
break;
case VIO_DRING_UNREG:
vsw_process_ctrl_dring_unreg_pkt(ldcp, &ctaskp->pktp);
break;
case VIO_ATTR_INFO:
vsw_process_ctrl_attr_pkt(ldcp, &ctaskp->pktp);
break;
case VNET_MCAST_INFO:
vsw_process_ctrl_mcst_pkt(ldcp, &ctaskp->pktp);
break;
case VIO_RDX:
vsw_process_ctrl_rdx_pkt(ldcp, &ctaskp->pktp);
break;
case VIO_DDS_INFO:
vsw_process_dds_msg(vswp, ldcp, &ctaskp->pktp);
break;
case VNET_PHYSLINK_INFO:
vsw_process_physlink_msg(ldcp, &ctaskp->pktp);
break;
default:
DERR(vswp, "%s: unknown vio_subtype_env (%x)\n", __func__, env);
}
kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
}
void
vsw_process_ctrl_ver_pkt(vsw_ldc_t *ldcp, void *pkt)
{
vio_ver_msg_t *ver_pkt;
vsw_t *vswp = ldcp->ldc_vswp;
D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
ver_pkt = (vio_ver_msg_t *)pkt;
switch (ver_pkt->tag.vio_subtype) {
case VIO_SUBTYPE_INFO:
D2(vswp, "vsw_process_ctrl_ver_pkt: VIO_SUBTYPE_INFO\n");
if ((ldcp->session_status & VSW_PEER_SESSION) &&
(ldcp->peer_session != ver_pkt->tag.vio_sid)) {
DERR(vswp, "%s: updating session id for chan %lld "
"from %llx to %llx", __func__, ldcp->ldc_id,
ldcp->peer_session, ver_pkt->tag.vio_sid);
}
ldcp->peer_session = ver_pkt->tag.vio_sid;
ldcp->session_status |= VSW_PEER_SESSION;
if (vsw_check_flag(ldcp, INBOUND, VSW_VER_INFO_RECV))
return;
if (ver_pkt->dev_class != VDEV_NETWORK) {
DERR(vswp, "%s: illegal device class %d", __func__,
ver_pkt->dev_class);
ver_pkt->tag.vio_sid = ldcp->local_session;
ver_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK;
DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt);
(void) vsw_send_msg(ldcp, (void *)ver_pkt,
sizeof (vio_ver_msg_t), B_TRUE);
ldcp->lane_in.lstate |= VSW_VER_NACK_SENT;
vsw_next_milestone(ldcp);
return;
} else {
ldcp->dev_class = ver_pkt->dev_class;
}
if (vsw_supported_version(ver_pkt) == 0) {
D2(vswp, "%s: accepted ver %d:%d", __func__,
ver_pkt->ver_major, ver_pkt->ver_minor);
ldcp->lane_in.ver_major = ver_pkt->ver_major;
ldcp->lane_in.ver_minor = ver_pkt->ver_minor;
ver_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK;
ldcp->lane_in.lstate |= VSW_VER_ACK_SENT;
if (vsw_obp_ver_proto_workaround == B_TRUE) {
vsw_send_ver(ldcp);
}
} else {
D2(vswp, "%s: replying with ver %d:%d", __func__,
ver_pkt->ver_major, ver_pkt->ver_minor);
ldcp->lane_in.ver_major = ver_pkt->ver_major;
ldcp->lane_in.ver_minor = ver_pkt->ver_minor;
ver_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK;
ldcp->lane_in.lstate |= VSW_VER_NACK_SENT;
}
DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt);
ver_pkt->tag.vio_sid = ldcp->local_session;
(void) vsw_send_msg(ldcp, (void *)ver_pkt,
sizeof (vio_ver_msg_t), B_TRUE);
vsw_next_milestone(ldcp);
break;
case VIO_SUBTYPE_ACK:
D2(vswp, "%s: VIO_SUBTYPE_ACK\n", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_VER_ACK_RECV))
return;
ldcp->lane_out.ver_major = ver_pkt->ver_major;
ldcp->lane_out.ver_minor = ver_pkt->ver_minor;
ldcp->lane_out.lstate |= VSW_VER_ACK_RECV;
vsw_next_milestone(ldcp);
break;
case VIO_SUBTYPE_NACK:
D2(vswp, "%s: VIO_SUBTYPE_NACK\n", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_VER_NACK_RECV))
return;
if ((ver_pkt->ver_major == 0) && (ver_pkt->ver_minor == 0)) {
DERR(vswp, "%s: peer unable to negotiate any "
"further.", __func__);
ldcp->lane_out.lstate |= VSW_VER_NACK_RECV;
vsw_next_milestone(ldcp);
return;
}
(void) vsw_supported_version(ver_pkt);
if ((ver_pkt->ver_major == 0) && (ver_pkt->ver_minor == 0)) {
DERR(vswp, "%s: version negotiation failed.\n",
__func__);
ldcp->lane_out.lstate |= VSW_VER_NACK_RECV;
vsw_next_milestone(ldcp);
} else {
ldcp->lane_out.ver_major = ver_pkt->ver_major;
ldcp->lane_out.ver_minor = ver_pkt->ver_minor;
D2(vswp, "%s: resending with updated values (%x, %x)",
__func__, ver_pkt->ver_major, ver_pkt->ver_minor);
ldcp->lane_out.lstate |= VSW_VER_INFO_SENT;
ver_pkt->tag.vio_sid = ldcp->local_session;
ver_pkt->tag.vio_subtype = VIO_SUBTYPE_INFO;
DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt);
(void) vsw_send_msg(ldcp, (void *)ver_pkt,
sizeof (vio_ver_msg_t), B_TRUE);
vsw_next_milestone(ldcp);
}
break;
default:
DERR(vswp, "%s: unknown vio_subtype %x\n", __func__,
ver_pkt->tag.vio_subtype);
}
D1(vswp, "%s(%lld): exit\n", __func__, ldcp->ldc_id);
}
static int
vsw_process_attr_info(vsw_ldc_t *ldcp, vnet_attr_msg_t *msg)
{
vsw_t *vswp = ldcp->ldc_vswp;
vsw_port_t *port = ldcp->ldc_port;
struct ether_addr ea;
uint64_t macaddr = 0;
lane_t *lane_out = &ldcp->lane_out;
lane_t *lane_in = &ldcp->lane_in;
uint32_t mtu;
int i;
uint8_t dring_mode;
D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
if (vsw_check_flag(ldcp, INBOUND, VSW_ATTR_INFO_RECV)) {
return (1);
}
if ((msg->xfer_mode != VIO_DESC_MODE) &&
(msg->xfer_mode != lane_out->xfer_mode)) {
D2(NULL, "%s: unknown mode %x\n", __func__, msg->xfer_mode);
return (1);
}
if ((msg->addr_type != ADDR_TYPE_MAC) || (msg->addr == 0)) {
D2(NULL, "%s: invalid addr_type %x, or address 0x%llx\n",
__func__, msg->addr_type, msg->addr);
return (1);
}
vnet_macaddr_ultostr(msg->addr, ea.ether_addr_octet);
if (ether_cmp(&ea, &port->p_macaddr) != 0) {
DERR(NULL, "%s: device supplied address "
"0x%llx doesn't match node address 0x%llx\n",
__func__, msg->addr, port->p_macaddr);
}
if ((VSW_VER_GTEQ(ldcp, 1, 2) &&
(msg->xfer_mode & VIO_DRING_MODE_V1_2)) ||
(VSW_VER_LT(ldcp, 1, 2) &&
(msg->xfer_mode == VIO_DRING_MODE_V1_0))) {
if (msg->ack_freq > 0) {
D2(NULL, "%s: non zero ack freq in SHM mode\n",
__func__);
return (1);
}
}
if (VSW_VER_GTEQ(ldcp, 1, 6)) {
if ((msg->options & VIO_RX_DRING_DATA) != 0 &&
vsw_mapin_avail(ldcp) == B_TRUE) {
dring_mode = VIO_RX_DRING_DATA;
} else if ((msg->options & VIO_TX_DRING) != 0) {
dring_mode = VIO_TX_DRING;
} else {
return (1);
}
if (lane_out->lstate & VSW_ATTR_ACK_RECV) {
if (msg->options != lane_out->dring_mode) {
return (1);
}
} else {
lane_out->dring_mode = dring_mode;
}
msg->options = dring_mode;
}
if (VSW_VER_GTEQ(ldcp, 1, 4)) {
if (msg->mtu < ETHERMAX) {
return (1);
}
mtu = MIN(msg->mtu, vswp->max_frame_size);
if (lane_out->lstate & VSW_ATTR_ACK_RECV) {
if (mtu != lane_out->mtu) {
return (1);
}
} else {
lane_out->mtu = mtu;
}
msg->mtu = mtu;
} else {
if (msg->mtu != lane_out->mtu) {
D2(NULL, "%s: invalid MTU (0x%llx)\n",
__func__, msg->mtu);
return (1);
}
}
lane_in->mtu = msg->mtu;
lane_in->addr = msg->addr;
lane_in->addr_type = msg->addr_type;
lane_in->xfer_mode = msg->xfer_mode;
lane_in->ack_freq = msg->ack_freq;
lane_in->physlink_update = msg->physlink_update;
lane_in->dring_mode = msg->options;
if (VSW_VER_GTEQ(ldcp, 1, 5)) {
if ((lane_in->physlink_update &
PHYSLINK_UPDATE_STATE_MASK) ==
PHYSLINK_UPDATE_STATE) {
if (vswp->smode & VSW_LAYER2) {
msg->physlink_update =
PHYSLINK_UPDATE_STATE_ACK;
ldcp->pls_negotiated = B_TRUE;
} else {
msg->physlink_update =
PHYSLINK_UPDATE_STATE_NACK;
ldcp->pls_negotiated = B_FALSE;
}
} else {
msg->physlink_update =
PHYSLINK_UPDATE_NONE;
ldcp->pls_negotiated = B_FALSE;
}
} else {
msg->physlink_update = PHYSLINK_UPDATE_NONE;
ldcp->pls_negotiated = B_FALSE;
}
macaddr = lane_in->addr;
for (i = ETHERADDRL - 1; i >= 0; i--) {
port->p_macaddr.ether_addr_octet[i] = macaddr & 0xFF;
macaddr >>= 8;
}
mutex_enter(&port->tx_lock);
if ((VSW_VER_GTEQ(ldcp, 1, 2) &&
(lane_in->xfer_mode & VIO_DRING_MODE_V1_2)) ||
(VSW_VER_LT(ldcp, 1, 2) &&
(lane_in->xfer_mode == VIO_DRING_MODE_V1_0))) {
D2(vswp, "%s: mode = VIO_DRING_MODE", __func__);
port->transmit = vsw_dringsend;
} else if (lane_in->xfer_mode == VIO_DESC_MODE) {
D2(vswp, "%s: mode = VIO_DESC_MODE", __func__);
vsw_create_privring(ldcp);
port->transmit = vsw_descrsend;
lane_out->xfer_mode = VIO_DESC_MODE;
}
if (VSW_VER_GTEQ(ldcp, 1, 3) &&
(lane_in->xfer_mode != VIO_DESC_MODE)) {
(void) atomic_swap_32(&port->p_hio_capable, B_TRUE);
} else {
(void) atomic_swap_32(&port->p_hio_capable, B_FALSE);
}
mutex_exit(&port->tx_lock);
return (0);
}
static int
vsw_process_attr_ack(vsw_ldc_t *ldcp, vnet_attr_msg_t *msg)
{
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lane_out = &ldcp->lane_out;
lane_t *lane_in = &ldcp->lane_in;
D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_ATTR_ACK_RECV)) {
return (1);
}
if (VSW_VER_GTEQ(ldcp, 1, 6)) {
if (lane_in->lstate & VSW_ATTR_ACK_SENT) {
if (lane_out->dring_mode != msg->options) {
return (1);
}
} else {
if ((msg->options & lane_out->dring_mode) == 0) {
return (1);
}
if ((msg->options & (VIO_TX_DRING|VIO_RX_DRING_DATA))
== (VIO_TX_DRING|VIO_RX_DRING_DATA)) {
return (1);
}
lane_out->dring_mode = msg->options;
}
}
if (VSW_VER_GTEQ(ldcp, 1, 4)) {
if (lane_in->lstate & VSW_ATTR_ACK_SENT) {
if (lane_out->mtu != msg->mtu) {
return (1);
}
} else {
if (msg->mtu <= lane_out->mtu) {
lane_out->mtu = msg->mtu;
} else {
return (1);
}
}
}
return (0);
}
void
vsw_process_ctrl_attr_pkt(vsw_ldc_t *ldcp, void *pkt)
{
vnet_attr_msg_t *attr_pkt;
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lane_out = &ldcp->lane_out;
lane_t *lane_in = &ldcp->lane_in;
int rv;
D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id);
attr_pkt = (vnet_attr_msg_t *)pkt;
switch (attr_pkt->tag.vio_subtype) {
case VIO_SUBTYPE_INFO:
rv = vsw_process_attr_info(ldcp, attr_pkt);
if (rv != 0) {
vsw_free_lane_resources(ldcp, INBOUND);
attr_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK;
ldcp->lane_in.lstate |= VSW_ATTR_NACK_SENT;
} else {
attr_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK;
lane_in->lstate |= VSW_ATTR_ACK_SENT;
}
attr_pkt->tag.vio_sid = ldcp->local_session;
DUMP_TAG_PTR((vio_msg_tag_t *)attr_pkt);
(void) vsw_send_msg(ldcp, (void *)attr_pkt,
sizeof (vnet_attr_msg_t), B_TRUE);
vsw_next_milestone(ldcp);
break;
case VIO_SUBTYPE_ACK:
rv = vsw_process_attr_ack(ldcp, attr_pkt);
if (rv != 0) {
return;
}
lane_out->lstate |= VSW_ATTR_ACK_RECV;
vsw_next_milestone(ldcp);
break;
case VIO_SUBTYPE_NACK:
D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_ATTR_NACK_RECV))
return;
lane_out->lstate |= VSW_ATTR_NACK_RECV;
vsw_next_milestone(ldcp);
break;
default:
DERR(vswp, "%s: unknown vio_subtype %x\n", __func__,
attr_pkt->tag.vio_subtype);
}
D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id);
}
static int
vsw_process_dring_reg_info(vsw_ldc_t *ldcp, vio_msg_tag_t *tagp)
{
int rv;
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lp = &ldcp->lane_out;
dring_info_t *dp = NULL;
D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
rv = vsw_check_flag(ldcp, INBOUND, VSW_DRING_INFO_RECV);
if (rv != 0) {
return (1);
}
if (VSW_VER_GTEQ(ldcp, 1, 6) &&
(lp->dring_mode != ((vio_dring_reg_msg_t *)tagp)->options)) {
DWARN(vswp, "%s(%lld): Rcvd dring reg option (%d), "
"negotiated mode (%d)\n", __func__, ldcp->ldc_id,
((vio_dring_reg_msg_t *)tagp)->options, lp->dring_mode);
return (1);
}
dp = vsw_map_dring(ldcp, (void *)tagp);
if (dp == NULL) {
return (1);
}
if (lp->dring_mode == VIO_RX_DRING_DATA) {
rv = vsw_map_data(ldcp, dp, (void *)tagp);
if (rv != 0) {
vsw_unmap_dring(ldcp);
return (1);
}
}
return (0);
}
static int
vsw_process_dring_reg_ack(vsw_ldc_t *ldcp, vio_msg_tag_t *tagp)
{
vsw_t *vswp = ldcp->ldc_vswp;
dring_info_t *dp;
D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_DRING_ACK_RECV)) {
return (1);
}
dp = ldcp->lane_out.dringp;
dp->ident = ((vio_dring_reg_msg_t *)tagp)->dring_ident;
return (0);
}
void
vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t *ldcp, void *pkt)
{
int rv;
int msgsize;
dring_info_t *dp;
vio_msg_tag_t *tagp = (vio_msg_tag_t *)pkt;
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lane_out = &ldcp->lane_out;
lane_t *lane_in = &ldcp->lane_in;
D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id);
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
rv = vsw_process_dring_reg_info(ldcp, tagp);
if (rv != 0) {
vsw_free_lane_resources(ldcp, INBOUND);
tagp->vio_subtype = VIO_SUBTYPE_NACK;
lane_in->lstate |= VSW_DRING_NACK_SENT;
} else {
tagp->vio_subtype = VIO_SUBTYPE_ACK;
lane_in->lstate |= VSW_DRING_ACK_SENT;
}
tagp->vio_sid = ldcp->local_session;
DUMP_TAG_PTR(tagp);
if (lane_out->dring_mode == VIO_RX_DRING_DATA) {
dp = lane_in->dringp;
msgsize =
VNET_DRING_REG_EXT_MSG_SIZE(dp->data_ncookies);
} else {
msgsize = sizeof (vio_dring_reg_msg_t);
}
(void) vsw_send_msg(ldcp, (void *)tagp, msgsize, B_TRUE);
vsw_next_milestone(ldcp);
break;
case VIO_SUBTYPE_ACK:
rv = vsw_process_dring_reg_ack(ldcp, tagp);
if (rv != 0) {
return;
}
lane_out->lstate |= VSW_DRING_ACK_RECV;
vsw_next_milestone(ldcp);
break;
case VIO_SUBTYPE_NACK:
D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_DRING_NACK_RECV))
return;
lane_out->lstate |= VSW_DRING_NACK_RECV;
vsw_next_milestone(ldcp);
break;
default:
DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__,
tagp->vio_subtype);
}
D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id);
}
void
vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t *ldcp, void *pkt)
{
vsw_t *vswp = ldcp->ldc_vswp;
vio_dring_unreg_msg_t *dring_pkt;
dring_pkt = (vio_dring_unreg_msg_t *)pkt;
D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
switch (dring_pkt->tag.vio_subtype) {
case VIO_SUBTYPE_INFO:
D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
DWARN(vswp, "%s: restarting handshake..", __func__);
break;
case VIO_SUBTYPE_ACK:
D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
DWARN(vswp, "%s: restarting handshake..", __func__);
break;
case VIO_SUBTYPE_NACK:
D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
DWARN(vswp, "%s: restarting handshake..", __func__);
break;
default:
DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__,
dring_pkt->tag.vio_subtype);
}
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
}
#define SND_MCST_NACK(ldcp, pkt) \
pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; \
pkt->tag.vio_sid = ldcp->local_session; \
(void) vsw_send_msg(ldcp, (void *)pkt, \
sizeof (vnet_mcast_msg_t), B_TRUE);
static void
vsw_process_ctrl_mcst_pkt(vsw_ldc_t *ldcp, void *pkt)
{
vnet_mcast_msg_t *mcst_pkt;
vsw_port_t *port = ldcp->ldc_port;
vsw_t *vswp = ldcp->ldc_vswp;
int i;
D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
mcst_pkt = (vnet_mcast_msg_t *)pkt;
switch (mcst_pkt->tag.vio_subtype) {
case VIO_SUBTYPE_INFO:
D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
if (vsw_check_flag(ldcp, INBOUND, VSW_MCST_INFO_RECV))
return;
for (i = 0; i < mcst_pkt->count; i++) {
if ((mcst_pkt->mca[i].ether_addr_octet[0] & 01) != 1) {
DERR(vswp, "%s: invalid multicast address",
__func__);
SND_MCST_NACK(ldcp, mcst_pkt);
return;
}
}
if (vsw_add_rem_mcst(mcst_pkt, port) != 0) {
SND_MCST_NACK(ldcp, mcst_pkt);
return;
}
mcst_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK;
mcst_pkt->tag.vio_sid = ldcp->local_session;
DUMP_TAG_PTR((vio_msg_tag_t *)mcst_pkt);
(void) vsw_send_msg(ldcp, (void *)mcst_pkt,
sizeof (vnet_mcast_msg_t), B_TRUE);
break;
case VIO_SUBTYPE_ACK:
DWARN(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_MCST_ACK_RECV))
return;
break;
case VIO_SUBTYPE_NACK:
DWARN(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_MCST_NACK_RECV))
return;
break;
default:
DERR(vswp, "%s: unknown vio_subtype %x\n", __func__,
mcst_pkt->tag.vio_subtype);
}
D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
}
static void
vsw_process_ctrl_rdx_pkt(vsw_ldc_t *ldcp, void *pkt)
{
vio_rdx_msg_t *rdx_pkt;
vsw_t *vswp = ldcp->ldc_vswp;
rdx_pkt = (vio_rdx_msg_t *)pkt;
D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id);
switch (rdx_pkt->tag.vio_subtype) {
case VIO_SUBTYPE_INFO:
D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
if (vsw_check_flag(ldcp, OUTBOUND, VSW_RDX_INFO_RECV))
return;
rdx_pkt->tag.vio_sid = ldcp->local_session;
rdx_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK;
DUMP_TAG_PTR((vio_msg_tag_t *)rdx_pkt);
ldcp->lane_out.lstate |= VSW_RDX_ACK_SENT;
(void) vsw_send_msg(ldcp, (void *)rdx_pkt,
sizeof (vio_rdx_msg_t), B_TRUE);
vsw_next_milestone(ldcp);
break;
case VIO_SUBTYPE_ACK:
DERR(vswp, "%s: Unexpected VIO_SUBTYPE_ACK", __func__);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
break;
case VIO_SUBTYPE_NACK:
D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
if (vsw_check_flag(ldcp, INBOUND, VSW_RDX_NACK_RECV))
return;
ldcp->lane_in.lstate |= VSW_RDX_NACK_RECV;
vsw_next_milestone(ldcp);
break;
default:
DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__,
rdx_pkt->tag.vio_subtype);
}
D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
}
static void
vsw_process_physlink_msg(vsw_ldc_t *ldcp, void *pkt)
{
vnet_physlink_msg_t *msgp;
vsw_t *vswp = ldcp->ldc_vswp;
msgp = (vnet_physlink_msg_t *)pkt;
D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id);
switch (msgp->tag.vio_subtype) {
case VIO_SUBTYPE_INFO:
DWARN(vswp, "%s: Unexpected VIO_SUBTYPE_INFO", __func__);
break;
case VIO_SUBTYPE_ACK:
D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
break;
case VIO_SUBTYPE_NACK:
D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
break;
default:
DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__,
msgp->tag.vio_subtype);
}
D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
}
static void
vsw_process_data_pkt(vsw_ldc_t *ldcp, void *dpkt, vio_msg_tag_t *tagp,
uint32_t msglen)
{
uint16_t env = tagp->vio_subtype_env;
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lp = &ldcp->lane_out;
uint8_t dring_mode = lp->dring_mode;
D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
if (ldcp->session_status & VSW_PEER_SESSION) {
if (ldcp->peer_session != tagp->vio_sid) {
DERR(vswp, "%s (chan %d): invalid session id (%llx)",
__func__, ldcp->ldc_id, tagp->vio_sid);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return;
}
}
if (ldcp->hphase != VSW_MILESTONE4) {
DERR(vswp, "%s: got data packet before handshake complete "
"hphase %d (%x: %x)", __func__, ldcp->hphase,
ldcp->lane_in.lstate, ldcp->lane_out.lstate);
DUMP_FLAGS(ldcp->lane_in.lstate);
DUMP_FLAGS(ldcp->lane_out.lstate);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
return;
}
if (dring_mode == VIO_TX_DRING) {
mutex_exit(&ldcp->ldc_cblock);
mutex_enter(&ldcp->ldc_rxlock);
}
if (env == VIO_DRING_DATA) {
ldcp->rx_dringdata(ldcp, dpkt);
} else if (env == VIO_PKT_DATA) {
ldcp->rx_pktdata(ldcp, dpkt, msglen);
} else if (env == VIO_DESC_DATA) {
vsw_process_data_ibnd_pkt(ldcp, dpkt);
} else {
DERR(vswp, "%s: unknown vio_subtype_env (%x)\n",
__func__, env);
}
if (dring_mode == VIO_TX_DRING) {
mutex_exit(&ldcp->ldc_rxlock);
mutex_enter(&ldcp->ldc_cblock);
}
D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
}
static void
vsw_process_pkt_data_nop(void *arg1, void *arg2, uint32_t msglen)
{
_NOTE(ARGUNUSED(arg1, arg2, msglen))
}
static void
vsw_process_pkt_data(void *arg1, void *arg2, uint32_t msglen)
{
vsw_ldc_t *ldcp = (vsw_ldc_t *)arg1;
vio_raw_data_msg_t *dpkt = (vio_raw_data_msg_t *)arg2;
uint32_t size;
mblk_t *mp;
vio_mblk_t *vmp;
vsw_t *vswp = ldcp->ldc_vswp;
vgen_stats_t *statsp = &ldcp->ldc_stats;
lane_t *lp = &ldcp->lane_out;
size = msglen - VIO_PKT_DATA_HDRSIZE;
if (size < ETHERMIN || size > lp->mtu) {
(void) atomic_inc_32(&statsp->rx_pri_fail);
DWARN(vswp, "%s(%lld) invalid size(%d)\n", __func__,
ldcp->ldc_id, size);
return;
}
vmp = vio_multipool_allocb(&ldcp->vmp, size + VLAN_TAGSZ);
if (vmp == NULL) {
mp = allocb(size + VLAN_TAGSZ, BPRI_MED);
if (mp == NULL) {
(void) atomic_inc_32(&statsp->rx_pri_fail);
DWARN(vswp, "%s(%lld) allocb failure, "
"unable to process priority frame\n", __func__,
ldcp->ldc_id);
return;
}
} else {
mp = vmp->mp;
}
mp->b_rptr += VLAN_TAGSZ;
bcopy(dpkt->data, mp->b_rptr, size);
mp->b_wptr = mp->b_rptr + size;
if (vmp != NULL) {
vmp->state = VIO_MBLK_HAS_DATA;
}
(void) atomic_inc_64(&statsp->rx_pri_packets);
(void) atomic_add_64(&statsp->rx_pri_bytes, size);
(void) vsw_vlan_frame_pretag(ldcp->ldc_port, VSW_VNETPORT, mp);
vswp->vsw_switch_frame(vswp, mp, VSW_VNETPORT, ldcp->ldc_port, NULL);
}
static void
vsw_process_data_ibnd_pkt(vsw_ldc_t *ldcp, void *pkt)
{
vnet_ibnd_desc_t *ibnd_desc;
dring_info_t *dp = NULL;
vsw_private_desc_t *priv_addr = NULL;
vsw_t *vswp = ldcp->ldc_vswp;
mblk_t *mp = NULL;
size_t nbytes = 0;
size_t off = 0;
uint64_t idx = 0;
uint32_t num = 1, len, datalen = 0;
uint64_t ncookies = 0;
int i, rv;
int j = 0;
D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
ibnd_desc = (vnet_ibnd_desc_t *)pkt;
switch (ibnd_desc->hdr.tag.vio_subtype) {
case VIO_SUBTYPE_INFO:
D1(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
if (vsw_check_flag(ldcp, INBOUND, VSW_DRING_INFO_RECV))
return;
datalen = ibnd_desc->nbytes;
D2(vswp, "%s(%lld): processing inband desc : "
": datalen 0x%lx", __func__, ldcp->ldc_id, datalen);
ncookies = ibnd_desc->ncookies;
nbytes = datalen;
if (nbytes & 0x7) {
off = 8 - (nbytes & 0x7);
nbytes += off;
}
mp = allocb(datalen + 8, BPRI_MED);
if (mp == NULL) {
DERR(vswp, "%s(%lld): allocb failed",
__func__, ldcp->ldc_id);
ldcp->ldc_stats.rx_allocb_fail++;
return;
}
mp->b_rptr += 8;
rv = ldc_mem_copy(ldcp->ldc_handle, (caddr_t)mp->b_rptr,
0, &nbytes, ibnd_desc->memcookie, (uint64_t)ncookies,
LDC_COPY_IN);
if (rv != 0) {
DERR(vswp, "%s(%d): unable to copy in data from "
"%d cookie(s)", __func__, ldcp->ldc_id, ncookies);
freemsg(mp);
ldcp->ldc_stats.ierrors++;
return;
}
D2(vswp, "%s(%d): copied in %ld bytes using %d cookies",
__func__, ldcp->ldc_id, nbytes, ncookies);
mp->b_wptr = mp->b_rptr + datalen;
ldcp->ldc_stats.ipackets++;
ldcp->ldc_stats.rbytes += datalen;
ibnd_desc->hdr.tag.vio_subtype = VIO_SUBTYPE_ACK;
ibnd_desc->hdr.tag.vio_sid = ldcp->local_session;
(void) vsw_send_msg(ldcp, (void *)ibnd_desc,
sizeof (vnet_ibnd_desc_t), B_TRUE);
(void) vsw_vlan_frame_pretag(ldcp->ldc_port, VSW_VNETPORT, mp);
vswp->vsw_switch_frame(vswp, mp, VSW_VNETPORT,
ldcp->ldc_port, NULL);
break;
case VIO_SUBTYPE_ACK:
D1(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
idx = ibnd_desc->hdr.desc_handle;
if (idx >= vsw_num_descriptors) {
cmn_err(CE_WARN, "!vsw%d: corrupted ACK received "
"(idx %ld)", vswp->instance, idx);
return;
}
if ((dp = ldcp->lane_out.dringp) == NULL) {
DERR(vswp, "%s: no dring found", __func__);
return;
}
len = dp->num_descriptors;
if (idx != dp->last_ack_recv) {
DWARN(vswp, "%s: dropped pkts detected, (%ld, %ld)",
__func__, dp->last_ack_recv, idx);
num = idx >= dp->last_ack_recv ?
idx - dp->last_ack_recv + 1:
(len - dp->last_ack_recv + 1) + idx;
}
for (i = dp->last_ack_recv; j < num; i = (i + 1) % len, j++) {
priv_addr = (vsw_private_desc_t *)dp->priv_addr + i;
mutex_enter(&priv_addr->dstate_lock);
if (priv_addr->dstate != VIO_DESC_READY) {
DERR(vswp, "%s: (%ld) desc at index %ld not "
"READY (0x%lx)", __func__,
ldcp->ldc_id, idx, priv_addr->dstate);
DERR(vswp, "%s: bound %d: ncookies %ld : "
"datalen %ld", __func__,
priv_addr->bound, priv_addr->ncookies,
priv_addr->datalen);
}
D2(vswp, "%s: (%lld) freeing descp at %lld", __func__,
ldcp->ldc_id, idx);
priv_addr->datalen = 0;
priv_addr->dstate = VIO_DESC_FREE;
mutex_exit(&priv_addr->dstate_lock);
}
dp->last_ack_recv = (idx + 1) % dp->num_descriptors;
break;
case VIO_SUBTYPE_NACK:
DERR(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
idx = ibnd_desc->hdr.desc_handle;
if (idx >= vsw_num_descriptors) {
DERR(vswp, "%s: corrupted NACK received (idx %lld)",
__func__, idx);
return;
}
if ((dp = ldcp->lane_out.dringp) == NULL) {
DERR(vswp, "%s: no dring found", __func__);
return;
}
priv_addr = (vsw_private_desc_t *)dp->priv_addr;
priv_addr += idx;
mutex_enter(&priv_addr->dstate_lock);
priv_addr->datalen = 0;
priv_addr->dstate = VIO_DESC_FREE;
mutex_exit(&priv_addr->dstate_lock);
break;
default:
DERR(vswp, "%s(%lld): Unknown vio_subtype %x\n", __func__,
ldcp->ldc_id, ibnd_desc->hdr.tag.vio_subtype);
}
D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id);
}
static void
vsw_process_err_pkt(vsw_ldc_t *ldcp, void *epkt, vio_msg_tag_t *tagp)
{
_NOTE(ARGUNUSED(epkt))
vsw_t *vswp = ldcp->ldc_vswp;
uint16_t env = tagp->vio_subtype_env;
D1(vswp, "%s (%lld): enter\n", __func__, ldcp->ldc_id);
D2(vswp, "%s: (%x) vio_subtype env", __func__, env);
D1(vswp, "%s (%lld): exit\n", __func__, ldcp->ldc_id);
}
int
vsw_portsend(vsw_port_t *port, mblk_t *mp)
{
mblk_t *mpt;
int count;
vsw_ldc_t *ldcp = port->ldcp;
int status = 0;
count = vsw_vlan_frame_untag(port, VSW_VNETPORT, &mp, &mpt);
if (count != 0) {
status = ldcp->tx(ldcp, mp, mpt, count);
}
return (status);
}
static uint32_t
vsw_get_pri_packets(vsw_t *vswp, mblk_t **np, mblk_t **npt,
mblk_t **hp, mblk_t **hpt)
{
mblk_t *tmp = NULL;
mblk_t *smp = NULL;
mblk_t *hmp = NULL;
mblk_t *hmpt = NULL;
mblk_t *nmp = NULL;
mblk_t *nmpt = NULL;
uint32_t count = 0;
int i;
struct ether_header *ehp;
uint32_t num_types;
uint16_t *types;
tmp = *np;
while (tmp != NULL) {
smp = tmp;
tmp = tmp->b_next;
smp->b_next = NULL;
smp->b_prev = NULL;
ehp = (struct ether_header *)smp->b_rptr;
num_types = vswp->pri_num_types;
types = vswp->pri_types;
for (i = 0; i < num_types; i++) {
if (ehp->ether_type == types[i]) {
if (hmp != NULL) {
hmpt->b_next = smp;
hmpt = smp;
} else {
hmp = hmpt = smp;
}
count++;
break;
}
}
if (i == num_types) {
if (nmp != NULL) {
nmpt->b_next = smp;
nmpt = smp;
} else {
nmp = nmpt = smp;
}
}
}
*hp = hmp;
*hpt = hmpt;
*np = nmp;
*npt = nmpt;
return (count);
}
static int
vsw_ldctx_pri(void *arg, mblk_t *mp, mblk_t *mpt, uint32_t count)
{
vsw_ldc_t *ldcp = (vsw_ldc_t *)arg;
mblk_t *tmp;
mblk_t *smp;
mblk_t *hmp;
mblk_t *hmpt;
mblk_t *nmp;
mblk_t *nmpt;
uint32_t n = 0;
vsw_t *vswp = ldcp->ldc_vswp;
ASSERT(VSW_PRI_ETH_DEFINED(vswp));
ASSERT(count != 0);
nmp = mp;
nmpt = mpt;
n = vsw_get_pri_packets(vswp, &nmp, &nmpt, &hmp, &hmpt);
tmp = hmp;
while (tmp != NULL) {
smp = tmp;
tmp = tmp->b_next;
smp->b_next = NULL;
vsw_ldcsend_pkt(ldcp, smp);
}
count -= n;
if (count == 0) {
return (0);
}
return (vsw_ldctx(ldcp, nmp, nmpt, count));
}
static int
vsw_ldctx(void *arg, mblk_t *mp, mblk_t *mpt, uint32_t count)
{
vsw_ldc_t *ldcp = (vsw_ldc_t *)arg;
mblk_t *tmp = NULL;
ASSERT(count != 0);
if (ldcp->tx_thread != NULL) {
mutex_enter(&ldcp->tx_thr_lock);
if ((ldcp->tx_cnt + count) >= vsw_max_tx_qcount) {
ldcp->ldc_stats.tx_qfull += count;
mutex_exit(&ldcp->tx_thr_lock);
freemsgchain(mp);
goto exit;
}
if (ldcp->tx_mhead == NULL) {
ldcp->tx_mhead = mp;
ldcp->tx_mtail = mpt;
cv_signal(&ldcp->tx_thr_cv);
} else {
ldcp->tx_mtail->b_next = mp;
ldcp->tx_mtail = mpt;
}
ldcp->tx_cnt += count;
mutex_exit(&ldcp->tx_thr_lock);
} else {
while (mp != NULL) {
tmp = mp->b_next;
mp->b_next = mp->b_prev = NULL;
(void) vsw_ldcsend(ldcp, mp, 1);
mp = tmp;
}
}
exit:
return (0);
}
static void
vsw_ldcsend_pkt(vsw_ldc_t *ldcp, mblk_t *mp)
{
vio_raw_data_msg_t *pkt;
mblk_t *bp;
mblk_t *nmp = NULL;
vio_mblk_t *vmp;
caddr_t dst;
uint32_t mblksz;
uint32_t size;
uint32_t nbytes;
int rv;
vsw_t *vswp = ldcp->ldc_vswp;
vgen_stats_t *statsp = &ldcp->ldc_stats;
if ((!(ldcp->lane_out.lstate & VSW_LANE_ACTIVE)) ||
(ldcp->ldc_status != LDC_UP) || (ldcp->ldc_handle == 0)) {
(void) atomic_inc_32(&statsp->tx_pri_fail);
DWARN(vswp, "%s(%lld) status(%d) lstate(0x%llx), dropping "
"packet\n", __func__, ldcp->ldc_id, ldcp->ldc_status,
ldcp->lane_out.lstate);
goto send_pkt_exit;
}
size = msgsize(mp);
if (size > (size_t)(ldcp->msglen - VIO_PKT_DATA_HDRSIZE)) {
(void) atomic_inc_32(&statsp->tx_pri_fail);
DWARN(vswp, "%s(%lld) invalid size(%d)\n", __func__,
ldcp->ldc_id, size);
goto send_pkt_exit;
}
if (size < ETHERMIN)
size = ETHERMIN;
vmp = vio_allocb(vswp->pri_tx_vmp);
if (vmp == NULL) {
(void) atomic_inc_32(&statsp->tx_pri_fail);
DWARN(vswp, "vio_allocb failed\n");
goto send_pkt_exit;
} else {
nmp = vmp->mp;
}
pkt = (vio_raw_data_msg_t *)nmp->b_rptr;
dst = (caddr_t)pkt->data;
for (bp = mp; bp != NULL; bp = bp->b_cont) {
mblksz = MBLKL(bp);
bcopy(bp->b_rptr, dst, mblksz);
dst += mblksz;
}
vmp->state = VIO_MBLK_HAS_DATA;
pkt->tag.vio_msgtype = VIO_TYPE_DATA;
pkt->tag.vio_subtype = VIO_SUBTYPE_INFO;
pkt->tag.vio_subtype_env = VIO_PKT_DATA;
pkt->tag.vio_sid = ldcp->local_session;
nbytes = VIO_PKT_DATA_HDRSIZE + size;
rv = vsw_send_msg(ldcp, (void *)pkt, nbytes, B_TRUE);
if (rv != 0) {
(void) atomic_inc_32(&statsp->tx_pri_fail);
DWARN(vswp, "%s(%lld) Error sending priority frame\n", __func__,
ldcp->ldc_id);
goto send_pkt_exit;
}
(void) atomic_inc_64(&statsp->tx_pri_packets);
(void) atomic_add_64(&statsp->tx_pri_packets, size);
send_pkt_exit:
if (nmp != NULL)
freemsg(nmp);
freemsg(mp);
}
static int
vsw_ldcsend(vsw_ldc_t *ldcp, mblk_t *mp, uint32_t retries)
{
int i;
int rc;
int status = 0;
vsw_port_t *port = ldcp->ldc_port;
dring_info_t *dp = NULL;
lane_t *lp = &ldcp->lane_out;
for (i = 0; i < retries; ) {
mutex_enter(&port->tx_lock);
if (port->transmit != NULL) {
status = (*port->transmit)(ldcp, mp);
}
if (status == LDC_TX_SUCCESS) {
mutex_exit(&port->tx_lock);
break;
}
i++;
if ((i == retries) && (status == LDC_TX_NORESOURCES)) {
ldcp->ldc_stats.oerrors++;
}
mutex_exit(&port->tx_lock);
if (status != LDC_TX_NORESOURCES) {
break;
}
if (((dp = ldcp->lane_out.dringp) != NULL) &&
((VSW_VER_GTEQ(ldcp, 1, 2) &&
(ldcp->lane_out.xfer_mode & VIO_DRING_MODE_V1_2)) ||
((VSW_VER_LT(ldcp, 1, 2) &&
(ldcp->lane_out.xfer_mode == VIO_DRING_MODE_V1_0))))) {
if (lp->dring_mode == VIO_TX_DRING) {
rc = vsw_reclaim_dring(dp, dp->end_idx);
}
} else {
break;
}
if ((rc == 0) && (i < retries)) {
delay(drv_usectohz(vsw_ldc_tx_delay));
}
}
freemsg(mp);
return (status);
}
static int
vsw_descrsend(vsw_ldc_t *ldcp, mblk_t *mp)
{
vsw_t *vswp = ldcp->ldc_vswp;
vnet_ibnd_desc_t ibnd_msg;
vsw_private_desc_t *priv_desc = NULL;
dring_info_t *dp = NULL;
size_t n, size = 0;
caddr_t bufp;
mblk_t *bp;
int idx, i;
int status = LDC_TX_SUCCESS;
static int warn_msg = 1;
lane_t *lp = &ldcp->lane_out;
D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
ASSERT(mp != NULL);
if ((!(ldcp->lane_out.lstate & VSW_LANE_ACTIVE)) ||
(ldcp->ldc_status != LDC_UP) || (ldcp->ldc_handle == 0)) {
DERR(vswp, "%s(%lld) status(%d) state (0x%llx), dropping pkt",
__func__, ldcp->ldc_id, ldcp->ldc_status,
ldcp->lane_out.lstate);
ldcp->ldc_stats.oerrors++;
return (LDC_TX_FAILURE);
}
if ((dp = ldcp->lane_out.dringp) == NULL) {
DERR(vswp, "%s(%lld): no dring for outbound lane",
__func__, ldcp->ldc_id);
DERR(vswp, "%s(%lld) status(%d) state (0x%llx)", __func__,
ldcp->ldc_id, ldcp->ldc_status, ldcp->lane_out.lstate);
ldcp->ldc_stats.oerrors++;
return (LDC_TX_FAILURE);
}
size = msgsize(mp);
if (size > (size_t)lp->mtu) {
DERR(vswp, "%s(%lld) invalid size (%ld)\n", __func__,
ldcp->ldc_id, size);
ldcp->ldc_stats.oerrors++;
return (LDC_TX_FAILURE);
}
if (vsw_dring_find_free_desc(dp, &priv_desc, &idx) != 0) {
if (warn_msg) {
DERR(vswp, "%s(%lld): no descriptor available for ring "
"at 0x%llx", __func__, ldcp->ldc_id, dp);
warn_msg = 0;
}
status = LDC_TX_NORESOURCES;
goto vsw_descrsend_free_exit;
} else {
D2(vswp, "%s(%lld): free private descriptor found at pos "
"%ld addr 0x%x\n", __func__, ldcp->ldc_id, idx, priv_desc);
warn_msg = 1;
}
bufp = priv_desc->datap;
for (bp = mp, n = 0; bp != NULL; bp = bp->b_cont) {
n = MBLKL(bp);
bcopy(bp->b_rptr, bufp, n);
bufp += n;
}
priv_desc->datalen = (size < (size_t)ETHERMIN) ? ETHERMIN : size;
ibnd_msg.hdr.tag.vio_msgtype = VIO_TYPE_DATA;
ibnd_msg.hdr.tag.vio_subtype = VIO_SUBTYPE_INFO;
ibnd_msg.hdr.tag.vio_subtype_env = VIO_DESC_DATA;
ibnd_msg.hdr.tag.vio_sid = ldcp->local_session;
for (i = 0; i < priv_desc->ncookies; i++) {
bcopy(&priv_desc->memcookie[i], &ibnd_msg.memcookie[i],
sizeof (ldc_mem_cookie_t));
}
ibnd_msg.hdr.desc_handle = idx;
ibnd_msg.ncookies = priv_desc->ncookies;
ibnd_msg.nbytes = size;
ldcp->ldc_stats.opackets++;
ldcp->ldc_stats.obytes += size;
(void) vsw_send_msg(ldcp, (void *)&ibnd_msg,
sizeof (vnet_ibnd_desc_t), B_TRUE);
vsw_descrsend_free_exit:
D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
return (status);
}
static void
vsw_send_ver(void *arg)
{
vsw_ldc_t *ldcp = (vsw_ldc_t *)arg;
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lp = &ldcp->lane_out;
vio_ver_msg_t ver_msg;
D1(vswp, "%s enter", __func__);
ver_msg.tag.vio_msgtype = VIO_TYPE_CTRL;
ver_msg.tag.vio_subtype = VIO_SUBTYPE_INFO;
ver_msg.tag.vio_subtype_env = VIO_VER_INFO;
ver_msg.tag.vio_sid = ldcp->local_session;
if (vsw_obp_ver_proto_workaround == B_FALSE) {
ver_msg.ver_major = vsw_versions[0].ver_major;
ver_msg.ver_minor = vsw_versions[0].ver_minor;
} else {
lane_t *lpi = &ldcp->lane_in;
ver_msg.ver_major = lpi->ver_major;
ver_msg.ver_minor = lpi->ver_minor;
}
ver_msg.dev_class = VDEV_NETWORK_SWITCH;
lp->lstate |= VSW_VER_INFO_SENT;
lp->ver_major = ver_msg.ver_major;
lp->ver_minor = ver_msg.ver_minor;
DUMP_TAG(ver_msg.tag);
(void) vsw_send_msg(ldcp, &ver_msg, sizeof (vio_ver_msg_t), B_TRUE);
D1(vswp, "%s (%d): exit", __func__, ldcp->ldc_id);
}
static void
vsw_send_attr(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lp = &ldcp->lane_out;
vnet_attr_msg_t attr_msg;
D1(vswp, "%s (%ld) enter", __func__, ldcp->ldc_id);
attr_msg.tag.vio_msgtype = VIO_TYPE_CTRL;
attr_msg.tag.vio_subtype = VIO_SUBTYPE_INFO;
attr_msg.tag.vio_subtype_env = VIO_ATTR_INFO;
attr_msg.tag.vio_sid = ldcp->local_session;
attr_msg.mtu = lp->mtu;
attr_msg.addr_type = lp->addr_type;
attr_msg.xfer_mode = lp->xfer_mode;
attr_msg.ack_freq = lp->xfer_mode;
attr_msg.options = lp->dring_mode;
READ_ENTER(&vswp->if_lockrw);
attr_msg.addr = vnet_macaddr_strtoul((vswp->if_addr).ether_addr_octet);
RW_EXIT(&vswp->if_lockrw);
ldcp->lane_out.lstate |= VSW_ATTR_INFO_SENT;
DUMP_TAG(attr_msg.tag);
(void) vsw_send_msg(ldcp, &attr_msg, sizeof (vnet_attr_msg_t), B_TRUE);
D1(vswp, "%s (%ld) exit", __func__, ldcp->ldc_id);
}
static void
vsw_send_dring_info(vsw_ldc_t *ldcp)
{
int msgsize;
void *msg;
vsw_t *vswp = ldcp->ldc_vswp;
vsw_port_t *port = ldcp->ldc_port;
lane_t *lp = &ldcp->lane_out;
vgen_stats_t *statsp = &ldcp->ldc_stats;
D1(vswp, "%s: (%ld) enter", __func__, ldcp->ldc_id);
statsp->dring_mode = lp->dring_mode;
if (lp->dring_mode == VIO_RX_DRING_DATA) {
port->transmit = vsw_dringsend_shm;
msg = (void *) vsw_create_rx_dring_info(ldcp);
if (msg == NULL) {
return;
}
msgsize =
VNET_DRING_REG_EXT_MSG_SIZE(lp->dringp->data_ncookies);
ldcp->rcv_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
vsw_ldc_rcv_worker, ldcp, 0, &p0, TS_RUN, maxclsyspri);
ldcp->rx_dringdata = vsw_process_dringdata_shm;
} else {
msg = (void *) vsw_create_tx_dring_info(ldcp);
if (msg == NULL) {
return;
}
msgsize = sizeof (vio_dring_reg_msg_t);
ldcp->msg_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
vsw_ldc_msg_worker, ldcp, 0, &p0, TS_RUN, maxclsyspri);
ldcp->rx_dringdata = vsw_process_dringdata;
}
lp->lstate |= VSW_DRING_INFO_SENT;
DUMP_TAG_PTR((vio_msg_tag_t *)msg);
(void) vsw_send_msg(ldcp, msg, msgsize, B_TRUE);
kmem_free(msg, msgsize);
D1(vswp, "%s: (%ld) exit", __func__, ldcp->ldc_id);
}
static void
vsw_send_rdx(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
vio_rdx_msg_t rdx_msg;
D1(vswp, "%s (%ld) enter", __func__, ldcp->ldc_id);
rdx_msg.tag.vio_msgtype = VIO_TYPE_CTRL;
rdx_msg.tag.vio_subtype = VIO_SUBTYPE_INFO;
rdx_msg.tag.vio_subtype_env = VIO_RDX;
rdx_msg.tag.vio_sid = ldcp->local_session;
ldcp->lane_in.lstate |= VSW_RDX_INFO_SENT;
DUMP_TAG(rdx_msg.tag);
(void) vsw_send_msg(ldcp, &rdx_msg, sizeof (vio_rdx_msg_t), B_TRUE);
D1(vswp, "%s (%ld) exit", __func__, ldcp->ldc_id);
}
mcst_addr_t *
vsw_del_addr(uint8_t devtype, void *arg, uint64_t addr)
{
vsw_t *vswp = NULL;
vsw_port_t *port = NULL;
mcst_addr_t *prev_p = NULL;
mcst_addr_t *curr_p = NULL;
D1(NULL, "%s: enter : devtype %d : addr 0x%llx",
__func__, devtype, addr);
if (devtype == VSW_VNETPORT) {
port = (vsw_port_t *)arg;
mutex_enter(&port->mca_lock);
prev_p = curr_p = port->mcap;
} else {
vswp = (vsw_t *)arg;
mutex_enter(&vswp->mca_lock);
prev_p = curr_p = vswp->mcap;
}
while (curr_p != NULL) {
if (curr_p->addr == addr) {
D2(NULL, "%s: address found", __func__);
if (prev_p == curr_p) {
if (devtype == VSW_VNETPORT)
port->mcap = curr_p->nextp;
else
vswp->mcap = curr_p->nextp;
} else {
prev_p->nextp = curr_p->nextp;
}
break;
} else {
prev_p = curr_p;
curr_p = curr_p->nextp;
}
}
if (devtype == VSW_VNETPORT)
mutex_exit(&port->mca_lock);
else
mutex_exit(&vswp->mca_lock);
D1(NULL, "%s: exit", __func__);
return (curr_p);
}
void
vsw_create_privring(vsw_ldc_t *ldcp)
{
dring_info_t *dp;
vsw_t *vswp = ldcp->ldc_vswp;
D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
dp = kmem_zalloc(sizeof (dring_info_t), KM_SLEEP);
mutex_init(&dp->dlock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&dp->restart_lock, NULL, MUTEX_DRIVER, NULL);
ldcp->lane_out.dringp = dp;
dp->pub_addr = NULL;
dp->priv_addr = kmem_zalloc(
(sizeof (vsw_private_desc_t) * vsw_num_descriptors), KM_SLEEP);
dp->num_descriptors = vsw_num_descriptors;
if (vsw_setup_tx_dring(ldcp, dp)) {
DERR(vswp, "%s: setup of ring failed", __func__);
vsw_destroy_tx_dring(ldcp);
return;
}
dp->end_idx = 0;
dp->restart_reqd = B_TRUE;
D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
}
static void
vsw_set_lane_attr(vsw_t *vswp, lane_t *lp)
{
bzero(lp, sizeof (lane_t));
READ_ENTER(&vswp->if_lockrw);
ether_copy(&(vswp->if_addr), &(lp->addr));
RW_EXIT(&vswp->if_lockrw);
lp->mtu = vswp->max_frame_size;
lp->addr_type = ADDR_TYPE_MAC;
lp->xfer_mode = VIO_DRING_MODE_V1_0;
lp->ack_freq = 0;
lp->seq_num = VNET_ISS;
}
static dring_info_t *
vsw_map_dring(vsw_ldc_t *ldcp, void *pkt)
{
dring_info_t *dp = NULL;
lane_t *lp = &ldcp->lane_out;
if (lp->dring_mode == VIO_RX_DRING_DATA) {
dp = vsw_map_tx_dring(ldcp, pkt);
} else {
dp = vsw_map_rx_dring(ldcp, pkt);
}
return (dp);
}
dring_info_t *
vsw_map_dring_cmn(vsw_ldc_t *ldcp, vio_dring_reg_msg_t *dring_pkt)
{
int rv;
dring_info_t *dp;
ldc_mem_info_t minfo;
vsw_t *vswp = ldcp->ldc_vswp;
if ((dring_pkt->num_descriptors == 0) ||
(dring_pkt->descriptor_size == 0) ||
(dring_pkt->ncookies != 1)) {
DERR(vswp, "%s (%lld): invalid dring info",
__func__, ldcp->ldc_id);
return (NULL);
}
dp = kmem_zalloc(sizeof (dring_info_t), KM_SLEEP);
dp->num_descriptors = dring_pkt->num_descriptors;
dp->descriptor_size = dring_pkt->descriptor_size;
dp->options = dring_pkt->options;
dp->dring_ncookies = dring_pkt->ncookies;
bcopy(&dring_pkt->cookie[0], &dp->dring_cookie[0],
sizeof (ldc_mem_cookie_t));
rv = ldc_mem_dring_map(ldcp->ldc_handle, &dp->dring_cookie[0],
dp->dring_ncookies, dp->num_descriptors, dp->descriptor_size,
LDC_DIRECT_MAP, &(dp->dring_handle));
if (rv != 0) {
goto fail;
}
rv = ldc_mem_dring_info(dp->dring_handle, &minfo);
if (rv != 0) {
goto fail;
}
dp->pub_addr = minfo.vaddr;
dp->dring_mtype = minfo.mtype;
dp->priv_addr = NULL;
dp->ident = ldcp->next_ident;
ldcp->next_ident++;
dring_pkt->dring_ident = dp->ident;
return (dp);
fail:
if (dp->dring_handle != 0) {
(void) ldc_mem_dring_unmap(dp->dring_handle);
}
kmem_free(dp, sizeof (*dp));
return (NULL);
}
static void
vsw_unmap_dring(vsw_ldc_t *ldcp)
{
lane_t *lane_out = &ldcp->lane_out;
if (lane_out->dring_mode == VIO_RX_DRING_DATA) {
vsw_unmap_tx_dring(ldcp);
} else {
vsw_unmap_rx_dring(ldcp);
}
}
static int
vsw_map_data(vsw_ldc_t *ldcp, dring_info_t *dp, void *pkt)
{
int rv;
vio_dring_reg_ext_msg_t *emsg;
vio_dring_reg_msg_t *msg = pkt;
uint8_t *buf = (uint8_t *)msg->cookie;
vsw_t *vswp = ldcp->ldc_vswp;
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 (1);
}
dp->data_ncookies = emsg->data_ncookies;
dp->data_sz = emsg->data_area_size;
rv = ldc_mem_alloc_handle(ldcp->ldc_handle, &dp->data_handle);
if (rv != 0) {
cmn_err(CE_WARN, "ldc_mem_alloc_handle failed\n");
DWARN(vswp, "%s (%lld) ldc_mem_alloc_handle() failed: %d\n",
__func__, ldcp->ldc_id, rv);
return (1);
}
rv = ldc_mem_map(dp->data_handle, emsg->data_cookie,
emsg->data_ncookies, LDC_DIRECT_MAP, LDC_MEM_R,
(caddr_t *)&dp->data_addr, NULL);
if (rv != 0) {
cmn_err(CE_WARN, "ldc_mem_map failed\n");
DWARN(vswp, "%s (%lld) ldc_mem_map() failed: %d\n",
__func__, ldcp->ldc_id, rv);
return (1);
}
rv = ldc_mem_info(dp->data_handle, &minfo);
if (rv != 0) {
cmn_err(CE_WARN, "ldc_mem_info failed\n");
DWARN(vswp, "%s (%lld) ldc_mem_info() failed: %d\n",
__func__, ldcp->ldc_id, rv);
return (1);
}
if (minfo.mtype != LDC_DIRECT_MAP) {
DWARN(vswp, "%s (%lld) mtype(%d) is not direct map\n",
__func__, ldcp->ldc_id, minfo.mtype);
return (1);
}
dp->data_cookie = kmem_zalloc(emsg->data_ncookies *
sizeof (ldc_mem_cookie_t), KM_SLEEP);
bcopy(emsg->data_cookie, dp->data_cookie,
emsg->data_ncookies * sizeof (ldc_mem_cookie_t));
return (0);
}
static void
vsw_free_lane_resources(vsw_ldc_t *ldcp, uint64_t dir)
{
lane_t *lp;
D1(ldcp->ldc_vswp, "%s (%lld): enter", __func__, ldcp->ldc_id);
if (dir == INBOUND) {
D2(ldcp->ldc_vswp, "%s: freeing INBOUND lane"
" of channel %lld", __func__, ldcp->ldc_id);
lp = &ldcp->lane_in;
} else {
D2(ldcp->ldc_vswp, "%s: freeing OUTBOUND lane"
" of channel %lld", __func__, ldcp->ldc_id);
lp = &ldcp->lane_out;
}
lp->lstate = VSW_LANE_INACTIV;
lp->seq_num = VNET_ISS;
if (dir == INBOUND) {
vsw_unmap_dring(ldcp);
} else {
vsw_destroy_dring(ldcp);
}
D1(ldcp->ldc_vswp, "%s (%lld): exit", __func__, ldcp->ldc_id);
}
static void
vsw_destroy_dring(vsw_ldc_t *ldcp)
{
lane_t *lp = &ldcp->lane_out;
if (lp->dring_mode == VIO_RX_DRING_DATA) {
vsw_destroy_rx_dring(ldcp);
} else {
vsw_destroy_tx_dring(ldcp);
}
}
static void
vsw_ldc_tx_worker(void *arg)
{
callb_cpr_t cprinfo;
vsw_ldc_t *ldcp = (vsw_ldc_t *)arg;
vsw_t *vswp = ldcp->ldc_vswp;
mblk_t *mp;
mblk_t *tmp;
D1(vswp, "%s(%lld):enter\n", __func__, ldcp->ldc_id);
CALLB_CPR_INIT(&cprinfo, &ldcp->tx_thr_lock, callb_generic_cpr,
"vnet_tx_thread");
mutex_enter(&ldcp->tx_thr_lock);
while (!(ldcp->tx_thr_flags & VSW_WTHR_STOP)) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
while (!(ldcp->tx_thr_flags & VSW_WTHR_STOP) &&
(ldcp->tx_mhead == NULL)) {
cv_wait(&ldcp->tx_thr_cv, &ldcp->tx_thr_lock);
}
CALLB_CPR_SAFE_END(&cprinfo, &ldcp->tx_thr_lock)
if (ldcp->tx_thr_flags & VSW_WTHR_STOP) {
D2(vswp, "%s(%lld):tx thread stopped\n",
__func__, ldcp->ldc_id);
break;
}
mp = ldcp->tx_mhead;
ldcp->tx_mhead = ldcp->tx_mtail = NULL;
ldcp->tx_cnt = 0;
mutex_exit(&ldcp->tx_thr_lock);
D2(vswp, "%s(%lld):calling vsw_ldcsend\n",
__func__, ldcp->ldc_id);
while (mp != NULL) {
tmp = mp->b_next;
mp->b_next = mp->b_prev = NULL;
(void) vsw_ldcsend(ldcp, mp, vsw_ldc_tx_retries);
mp = tmp;
}
mutex_enter(&ldcp->tx_thr_lock);
}
ldcp->tx_thr_flags &= ~VSW_WTHR_STOP;
ldcp->tx_thread = NULL;
CALLB_CPR_EXIT(&cprinfo);
D1(vswp, "%s(%lld):exit\n", __func__, ldcp->ldc_id);
thread_exit();
}
static void
vsw_stop_tx_thread(vsw_ldc_t *ldcp)
{
kt_did_t tid = 0;
vsw_t *vswp = ldcp->ldc_vswp;
D1(vswp, "%s(%lld):enter\n", __func__, ldcp->ldc_id);
mutex_enter(&ldcp->tx_thr_lock);
if (ldcp->tx_thread != NULL) {
tid = ldcp->tx_thread->t_did;
ldcp->tx_thr_flags |= VSW_WTHR_STOP;
cv_signal(&ldcp->tx_thr_cv);
}
mutex_exit(&ldcp->tx_thr_lock);
if (tid != 0) {
thread_join(tid);
}
D1(vswp, "%s(%lld):exit\n", __func__, ldcp->ldc_id);
}
static int
vsw_mapin_avail(vsw_ldc_t *ldcp)
{
int rv;
ldc_info_t info;
uint64_t mapin_sz_req;
uint64_t dblk_sz;
vsw_t *vswp = ldcp->ldc_vswp;
rv = ldc_info(ldcp->ldc_handle, &info);
if (rv != 0) {
return (B_FALSE);
}
dblk_sz = RXDRING_DBLK_SZ(vswp->max_frame_size);
mapin_sz_req = (VSW_RXDRING_NRBUFS * dblk_sz);
if (info.direct_map_size_max >= mapin_sz_req) {
return (B_TRUE);
}
return (B_FALSE);
}
static void
display_state(void)
{
vsw_t *vswp;
vsw_port_list_t *plist;
vsw_port_t *port;
vsw_ldc_t *ldcp;
extern vsw_t *vsw_head;
cmn_err(CE_NOTE, "***** system state *****");
for (vswp = vsw_head; vswp; vswp = vswp->next) {
plist = &vswp->plist;
READ_ENTER(&plist->lockrw);
cmn_err(CE_CONT, "vsw instance %d has %d ports attached\n",
vswp->instance, plist->num_ports);
for (port = plist->head; port != NULL; port = port->p_next) {
cmn_err(CE_CONT, "port %d : %d ldcs attached\n",
port->p_instance, port->num_ldcs);
ldcp = port->ldcp;
cmn_err(CE_CONT, "chan %lu : dev %d : "
"status %d : phase %u\n",
ldcp->ldc_id, ldcp->dev_class,
ldcp->ldc_status, ldcp->hphase);
cmn_err(CE_CONT, "chan %lu : lsession %lu : "
"psession %lu\n", ldcp->ldc_id,
ldcp->local_session, ldcp->peer_session);
cmn_err(CE_CONT, "Inbound lane:\n");
display_lane(&ldcp->lane_in);
cmn_err(CE_CONT, "Outbound lane:\n");
display_lane(&ldcp->lane_out);
}
RW_EXIT(&plist->lockrw);
}
cmn_err(CE_NOTE, "***** system state *****");
}
static void
display_lane(lane_t *lp)
{
dring_info_t *drp = lp->dringp;
cmn_err(CE_CONT, "ver 0x%x:0x%x : state %lx : mtu 0x%lx\n",
lp->ver_major, lp->ver_minor, lp->lstate, lp->mtu);
cmn_err(CE_CONT, "addr_type %d : addr 0x%lx : xmode %d\n",
lp->addr_type, lp->addr, lp->xfer_mode);
cmn_err(CE_CONT, "dringp 0x%lx\n", (uint64_t)lp->dringp);
cmn_err(CE_CONT, "Dring info:\n");
cmn_err(CE_CONT, "\tnum_desc %u : dsize %u\n",
drp->num_descriptors, drp->descriptor_size);
cmn_err(CE_CONT, "\thandle 0x%lx\n", drp->dring_handle);
cmn_err(CE_CONT, "\tpub_addr 0x%lx : priv_addr 0x%lx\n",
(uint64_t)drp->pub_addr, (uint64_t)drp->priv_addr);
cmn_err(CE_CONT, "\tident 0x%lx : end_idx %lu\n",
drp->ident, drp->end_idx);
display_ring(drp);
}
static void
display_ring(dring_info_t *dringp)
{
uint64_t i;
uint64_t priv_count = 0;
uint64_t pub_count = 0;
vnet_public_desc_t *pub_addr = NULL;
vsw_private_desc_t *priv_addr = NULL;
for (i = 0; i < vsw_num_descriptors; i++) {
if (dringp->pub_addr != NULL) {
pub_addr = (vnet_public_desc_t *)dringp->pub_addr + i;
if (pub_addr->hdr.dstate == VIO_DESC_FREE)
pub_count++;
}
if (dringp->priv_addr != NULL) {
priv_addr = (vsw_private_desc_t *)dringp->priv_addr + i;
if (priv_addr->dstate == VIO_DESC_FREE)
priv_count++;
}
}
cmn_err(CE_CONT, "\t%lu elements: %lu priv free: %lu pub free\n",
i, priv_count, pub_count);
}
static void
dump_flags(uint64_t state)
{
int i;
typedef struct flag_name {
int flag_val;
char *flag_name;
} flag_name_t;
flag_name_t flags[] = {
VSW_VER_INFO_SENT, "VSW_VER_INFO_SENT",
VSW_VER_INFO_RECV, "VSW_VER_INFO_RECV",
VSW_VER_ACK_RECV, "VSW_VER_ACK_RECV",
VSW_VER_ACK_SENT, "VSW_VER_ACK_SENT",
VSW_VER_NACK_RECV, "VSW_VER_NACK_RECV",
VSW_VER_NACK_SENT, "VSW_VER_NACK_SENT",
VSW_ATTR_INFO_SENT, "VSW_ATTR_INFO_SENT",
VSW_ATTR_INFO_RECV, "VSW_ATTR_INFO_RECV",
VSW_ATTR_ACK_SENT, "VSW_ATTR_ACK_SENT",
VSW_ATTR_ACK_RECV, "VSW_ATTR_ACK_RECV",
VSW_ATTR_NACK_SENT, "VSW_ATTR_NACK_SENT",
VSW_ATTR_NACK_RECV, "VSW_ATTR_NACK_RECV",
VSW_DRING_INFO_SENT, "VSW_DRING_INFO_SENT",
VSW_DRING_INFO_RECV, "VSW_DRING_INFO_RECV",
VSW_DRING_ACK_SENT, "VSW_DRING_ACK_SENT",
VSW_DRING_ACK_RECV, "VSW_DRING_ACK_RECV",
VSW_DRING_NACK_SENT, "VSW_DRING_NACK_SENT",
VSW_DRING_NACK_RECV, "VSW_DRING_NACK_RECV",
VSW_RDX_INFO_SENT, "VSW_RDX_INFO_SENT",
VSW_RDX_INFO_RECV, "VSW_RDX_INFO_RECV",
VSW_RDX_ACK_SENT, "VSW_RDX_ACK_SENT",
VSW_RDX_ACK_RECV, "VSW_RDX_ACK_RECV",
VSW_RDX_NACK_SENT, "VSW_RDX_NACK_SENT",
VSW_RDX_NACK_RECV, "VSW_RDX_NACK_RECV",
VSW_MCST_INFO_SENT, "VSW_MCST_INFO_SENT",
VSW_MCST_INFO_RECV, "VSW_MCST_INFO_RECV",
VSW_MCST_ACK_SENT, "VSW_MCST_ACK_SENT",
VSW_MCST_ACK_RECV, "VSW_MCST_ACK_RECV",
VSW_MCST_NACK_SENT, "VSW_MCST_NACK_SENT",
VSW_MCST_NACK_RECV, "VSW_MCST_NACK_RECV",
VSW_LANE_ACTIVE, "VSW_LANE_ACTIVE"};
DERR(NULL, "DUMP_FLAGS: %llx\n", state);
for (i = 0; i < sizeof (flags)/sizeof (flag_name_t); i++) {
if (state & flags[i].flag_val)
DERR(NULL, "DUMP_FLAGS %s", flags[i].flag_name);
}
}