diff options
author | WENTAO YANG <Wentao.Yang@Sun.COM> | 2009-05-18 11:55:00 -0700 |
---|---|---|
committer | WENTAO YANG <Wentao.Yang@Sun.COM> | 2009-05-18 11:55:00 -0700 |
commit | 6d6de4ee7da84f30d7d942184d5386d89b304030 (patch) | |
tree | 1abd436d6bc0ccde689fa4ad14aef67a729e1eed | |
parent | 56e2cc86321ec889bf83a888d902c60d6fb2ef8d (diff) | |
download | illumos-gate-6d6de4ee7da84f30d7d942184d5386d89b304030.tar.gz |
6835623 hybrid mode vnet panics in nxge while its channel to vswitch is reset
-rw-r--r-- | usr/src/uts/sun4v/io/vnet.c | 21 | ||||
-rw-r--r-- | usr/src/uts/sun4v/io/vnet_dds.c | 19 | ||||
-rw-r--r-- | usr/src/uts/sun4v/io/vnet_gen.c | 70 |
3 files changed, 84 insertions, 26 deletions
diff --git a/usr/src/uts/sun4v/io/vnet.c b/usr/src/uts/sun4v/io/vnet.c index 21c8992617..681c586121 100644 --- a/usr/src/uts/sun4v/io/vnet.c +++ b/usr/src/uts/sun4v/io/vnet.c @@ -102,6 +102,7 @@ static void vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err); /* Exported to vnet_gen */ int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu); void vnet_link_update(vnet_t *vnetp, link_state_t link_state); +void vnet_dds_cleanup_hio(vnet_t *vnetp); static kstat_t *vnet_hio_setup_kstats(char *ks_mod, char *ks_name, vnet_res_t *vresp); @@ -128,6 +129,7 @@ extern int vdds_init(vnet_t *vnetp); extern void vdds_cleanup(vnet_t *vnetp); extern void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg); extern void vdds_cleanup_hybrid_res(void *arg); +extern void vdds_cleanup_hio(vnet_t *vnetp); #define DRV_NAME "vnet" #define VNET_FDBE_REFHOLD(p) \ @@ -1342,6 +1344,15 @@ vnet_send_dds_msg(vnet_t *vnetp, void *dmsg) } /* + * vnet_cleanup_hio -- an interface called by vgen to cleanup hio resources. + */ +void +vnet_dds_cleanup_hio(vnet_t *vnetp) +{ + vdds_cleanup_hio(vnetp); +} + +/* * vnet_handle_res_err -- A callback function called by a resource * to report an error. For example, vgen can call to report * an LDC down/reset event. This will trigger cleanup of associated @@ -1353,7 +1364,6 @@ vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err) { vnet_res_t *vresp = (vnet_res_t *)vrh; vnet_t *vnetp = vresp->vnetp; - int rv; if (vnetp == NULL) { return; @@ -1362,13 +1372,8 @@ vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err) (vresp->type != VIO_NET_RES_HYBRID)) { return; } - rv = ddi_taskq_dispatch(vnetp->taskqp, vdds_cleanup_hybrid_res, - vnetp, DDI_NOSLEEP); - if (rv != DDI_SUCCESS) { - cmn_err(CE_WARN, - "vnet%d:Failed to dispatch task to cleanup hybrid resource", - vnetp->instance); - } + + vdds_cleanup_hio(vnetp); } /* diff --git a/usr/src/uts/sun4v/io/vnet_dds.c b/usr/src/uts/sun4v/io/vnet_dds.c index 5fa8057b3d..c3548db771 100644 --- a/usr/src/uts/sun4v/io/vnet_dds.c +++ b/usr/src/uts/sun4v/io/vnet_dds.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -87,6 +87,7 @@ int vdds_init(vnet_t *vnetp); void vdds_cleanup(vnet_t *vnetp); void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg); void vdds_cleanup_hybrid_res(void *arg); +void vdds_cleanup_hio(vnet_t *vnetp); /* Support functions to create/destory Hybrid device */ static dev_info_t *vdds_create_niu_node(uint64_t cookie, @@ -274,6 +275,22 @@ vdds_cleanup_hybrid_res(void *arg) } /* + * vdds_cleanup_hio -- An interface to cleanup the hio resources before + * resetting the vswitch port. + */ +void +vdds_cleanup_hio(vnet_t *vnetp) +{ + vnet_dds_info_t *vdds = &vnetp->vdds_info; + + /* Wait for any pending vdds tasks to complete */ + ddi_taskq_wait(vdds->dds_taskqp); + vdds_cleanup_hybrid_res(vnetp); + /* Wait for the cleanup task to complete */ + ddi_taskq_wait(vdds->dds_taskqp); +} + +/* * vdds_process_dds_msg -- Process a DDS message. */ void diff --git a/usr/src/uts/sun4v/io/vnet_gen.c b/usr/src/uts/sun4v/io/vnet_gen.c index 6c76683812..c907e9a831 100644 --- a/usr/src/uts/sun4v/io/vnet_gen.c +++ b/usr/src/uts/sun4v/io/vnet_gen.c @@ -122,7 +122,7 @@ static int vgen_update_port(vgen_t *vgenp, md_t *curr_mdp, static uint64_t vgen_port_stat(vgen_port_t *portp, uint_t stat); static void vgen_port_reset(vgen_port_t *portp); static void vgen_reset_vsw_port(vgen_t *vgenp); - +static void vgen_ldc_reset(vgen_ldc_t *ldcp); static int vgen_ldc_attach(vgen_port_t *portp, uint64_t ldc_id); static void vgen_ldc_detach(vgen_ldc_t *ldcp); static int vgen_alloc_tx_ring(vgen_ldc_t *ldcp); @@ -224,6 +224,7 @@ static int vgen_dds_rx(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp); /* externs */ extern void vnet_dds_rx(void *arg, void *dmsg); +extern void vnet_dds_cleanup_hio(vnet_t *vnetp); extern int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu); extern void vnet_link_update(vnet_t *vnetp, link_state_t link_state); @@ -2378,9 +2379,12 @@ vgen_update_md_prop(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex) if (updated & MD_pls) { /* enable/disable physical link state updates */ vnetp->pls_update = pls_update; + mutex_exit(&vgenp->lock); /* reset vsw-port to re-negotiate with the updated prop. */ vgen_reset_vsw_port(vgenp); + + mutex_enter(&vgenp->lock); } } @@ -4011,7 +4015,7 @@ ldc_cb_ret: * If the timeout handler did run, then it would just * return as cancel_htid is set. */ - DBG2(vgenp, ldcp, "calling cance_htid =0x%X \n", cancel_htid); + DBG2(vgenp, ldcp, "cancel_htid =0x%X \n", cancel_htid); (void) untimeout(cancel_htid); mutex_enter(&ldcp->cblock); /* clear it only if its the same as the one we cancelled */ @@ -4087,7 +4091,6 @@ vgen_evt_read: * If sid mismatch is detected, * reset the channel. */ - ldcp->need_ldc_reset = B_TRUE; goto vgen_evtread_error; } } @@ -4137,7 +4140,7 @@ vgen_evtread_error: } vgen_handle_evt_reset(ldcp); } else if (rv) { - vgen_handshake_retry(ldcp); + vgen_ldc_reset(ldcp); } /* @@ -4157,8 +4160,7 @@ vgen_evtread_error: * If the timeout handler did run, then it would just * return as cancel_htid is set. */ - DBG2(vgenp, ldcp, "calling cance_htid =0x%X \n", - cancel_htid); + DBG2(vgenp, ldcp, "cancel_htid =0x%X \n", cancel_htid); (void) untimeout(cancel_htid); /* @@ -4665,8 +4667,7 @@ vgen_vlan_unaware_port_reset(vgen_port_t *portp) */ if (ldcp->hphase == VH_DONE && VGEN_VER_LT(ldcp, 1, 3) && (portp->nvids != 0 || portp->pvid != vnetp->pvid)) { - ldcp->need_ldc_reset = B_TRUE; - vgen_handshake_retry(ldcp); + vgen_ldc_reset(ldcp); } mutex_exit(&ldcp->cblock); @@ -4695,8 +4696,7 @@ vgen_port_reset(vgen_port_t *portp) mutex_enter(&ldcp->cblock); - ldcp->need_ldc_reset = B_TRUE; - vgen_handshake_retry(ldcp); + vgen_ldc_reset(ldcp); mutex_exit(&ldcp->cblock); @@ -5934,6 +5934,15 @@ vgen_handle_ctrlmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp) break; case VIO_DDS_INFO: + /* + * If we are in the process of resetting the vswitch channel, + * drop the dds message. A new handshake will be initiated + * when the channel comes back up after the reset and dds + * negotiation can then continue. + */ + if (ldcp->need_ldc_reset == B_TRUE) { + break; + } rv = vgen_dds_rx(ldcp, tagp); break; @@ -6828,8 +6837,7 @@ vgen_ldc_watchdog(void *arg) } #endif mutex_enter(&ldcp->cblock); - ldcp->need_ldc_reset = B_TRUE; - vgen_handshake_retry(ldcp); + vgen_ldc_reset(ldcp); mutex_exit(&ldcp->cblock); if (ldcp->need_resched) { vio_net_tx_update_t vtx_update = @@ -6876,7 +6884,6 @@ vgen_check_datamsg_seq(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp) "next_rxseq(0x%lx) != seq_num(0x%lx)\n", ldcp->next_rxseq, seq_num); - ldcp->need_ldc_reset = B_TRUE; return (EINVAL); } @@ -6916,8 +6923,7 @@ vgen_hwatchdog(void *arg) vgen_ldc_t *ldcp = (vgen_ldc_t *)arg; vgen_t *vgenp = LDC_TO_VGEN(ldcp); - DWARN(vgenp, ldcp, - "handshake timeout ldc(%lx) phase(%x) state(%x)\n", + DWARN(vgenp, ldcp, "handshake timeout phase(%x) state(%x)\n", ldcp->hphase, ldcp->hstate); mutex_enter(&ldcp->cblock); @@ -6927,8 +6933,7 @@ vgen_hwatchdog(void *arg) return; } ldcp->htid = 0; - ldcp->need_ldc_reset = B_TRUE; - vgen_handshake_retry(ldcp); + vgen_ldc_reset(ldcp); mutex_exit(&ldcp->cblock); } @@ -7197,6 +7202,37 @@ vgen_dsend_exit: } +static void +vgen_ldc_reset(vgen_ldc_t *ldcp) +{ + vnet_t *vnetp = LDC_TO_VNET(ldcp); + vgen_t *vgenp = LDC_TO_VGEN(ldcp); + + ASSERT(MUTEX_HELD(&ldcp->cblock)); + + if (ldcp->need_ldc_reset == B_TRUE) { + /* another thread is already in the process of resetting */ + return; + } + + /* Set the flag to indicate reset is in progress */ + ldcp->need_ldc_reset = B_TRUE; + + if (ldcp->portp == vgenp->vsw_portp) { + mutex_exit(&ldcp->cblock); + /* + * Now cleanup any HIO resources; the above flag also tells + * the code that handles dds messages to drop any new msgs + * that arrive while we are cleaning up and resetting the + * channel. + */ + vnet_dds_cleanup_hio(vnetp); + mutex_enter(&ldcp->cblock); + } + + vgen_handshake_retry(ldcp); +} + #if DEBUG /* |