Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
Show First 20 Lines • Show All 450 Lines • ▼ Show 20 Lines | netvsc_attach(device_t dev) | ||||
struct ifnet *ifp = NULL; | struct ifnet *ifp = NULL; | ||||
int error, ring_cnt, tx_ring_cnt; | int error, ring_cnt, tx_ring_cnt; | ||||
int tso_maxlen; | int tso_maxlen; | ||||
sc->hn_dev = dev; | sc->hn_dev = dev; | ||||
sc->hn_prichan = vmbus_get_channel(dev); | sc->hn_prichan = vmbus_get_channel(dev); | ||||
HN_LOCK_INIT(sc); | HN_LOCK_INIT(sc); | ||||
/* | |||||
* Setup taskqueue for transmission. | |||||
*/ | |||||
if (hn_tx_taskq == NULL) { | if (hn_tx_taskq == NULL) { | ||||
sc->hn_tx_taskq = taskqueue_create("hn_tx", M_WAITOK, | sc->hn_tx_taskq = taskqueue_create("hn_tx", M_WAITOK, | ||||
taskqueue_thread_enqueue, &sc->hn_tx_taskq); | taskqueue_thread_enqueue, &sc->hn_tx_taskq); | ||||
if (hn_bind_tx_taskq >= 0) { | if (hn_bind_tx_taskq >= 0) { | ||||
int cpu = hn_bind_tx_taskq; | int cpu = hn_bind_tx_taskq; | ||||
cpuset_t cpu_set; | cpuset_t cpu_set; | ||||
if (cpu > mp_ncpus - 1) | if (cpu > mp_ncpus - 1) | ||||
cpu = mp_ncpus - 1; | cpu = mp_ncpus - 1; | ||||
CPU_SETOF(cpu, &cpu_set); | CPU_SETOF(cpu, &cpu_set); | ||||
taskqueue_start_threads_cpuset(&sc->hn_tx_taskq, 1, | taskqueue_start_threads_cpuset(&sc->hn_tx_taskq, 1, | ||||
PI_NET, &cpu_set, "%s tx", | PI_NET, &cpu_set, "%s tx", | ||||
device_get_nameunit(dev)); | device_get_nameunit(dev)); | ||||
} else { | } else { | ||||
taskqueue_start_threads(&sc->hn_tx_taskq, 1, PI_NET, | taskqueue_start_threads(&sc->hn_tx_taskq, 1, PI_NET, | ||||
"%s tx", device_get_nameunit(dev)); | "%s tx", device_get_nameunit(dev)); | ||||
} | } | ||||
} else { | } else { | ||||
sc->hn_tx_taskq = hn_tx_taskq; | sc->hn_tx_taskq = hn_tx_taskq; | ||||
} | } | ||||
/* | |||||
* Allocate ifnet and setup its name earlier, so that if_printf | |||||
* can be used by functions, which will be called after | |||||
* ether_ifattach(). | |||||
*/ | |||||
ifp = sc->hn_ifp = if_alloc(IFT_ETHER); | ifp = sc->hn_ifp = if_alloc(IFT_ETHER); | ||||
ifp->if_softc = sc; | ifp->if_softc = sc; | ||||
if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | ||||
/* | /* | ||||
* Initialize ifmedia earlier so that it can be unconditionally | |||||
* destroyed, if error happened later on. | |||||
*/ | |||||
ifmedia_init(&sc->hn_media, 0, hn_ifmedia_upd, hn_ifmedia_sts); | |||||
/* | |||||
* Figure out the # of RX rings (ring_cnt) and the # of TX rings | * Figure out the # of RX rings (ring_cnt) and the # of TX rings | ||||
* to use (tx_ring_cnt). | * to use (tx_ring_cnt). | ||||
* | * | ||||
* NOTE: | * NOTE: | ||||
* The # of RX rings to use is same as the # of channels to use. | * The # of RX rings to use is same as the # of channels to use. | ||||
*/ | */ | ||||
ring_cnt = hn_chan_cnt; | ring_cnt = hn_chan_cnt; | ||||
if (ring_cnt <= 0) { | if (ring_cnt <= 0) { | ||||
Show All 13 Lines | if (hn_use_if_start) { | ||||
tx_ring_cnt = 1; | tx_ring_cnt = 1; | ||||
} | } | ||||
/* | /* | ||||
* Set the leader CPU for channels. | * Set the leader CPU for channels. | ||||
*/ | */ | ||||
sc->hn_cpu = atomic_fetchadd_int(&hn_cpu_index, ring_cnt) % mp_ncpus; | sc->hn_cpu = atomic_fetchadd_int(&hn_cpu_index, ring_cnt) % mp_ncpus; | ||||
/* | |||||
* Create enough TX/RX rings, even if only limited number of | |||||
* channels can be allocated. | |||||
*/ | |||||
error = hn_create_tx_data(sc, tx_ring_cnt); | error = hn_create_tx_data(sc, tx_ring_cnt); | ||||
if (error) | if (error) | ||||
goto failed; | goto failed; | ||||
error = hn_create_rx_data(sc, ring_cnt); | error = hn_create_rx_data(sc, ring_cnt); | ||||
if (error) | if (error) | ||||
goto failed; | goto failed; | ||||
/* | /* | ||||
* Create transaction context for NVS and RNDIS transactions. | * Create transaction context for NVS and RNDIS transactions. | ||||
*/ | */ | ||||
sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev), | sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev), | ||||
HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0); | HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0); | ||||
if (sc->hn_xact == NULL) | if (sc->hn_xact == NULL) | ||||
goto failed; | goto failed; | ||||
/* | /* | ||||
* Attach the synthetic parts, i.e. NVS and RNDIS. | * Attach the synthetic parts, i.e. NVS and RNDIS. | ||||
*/ | */ | ||||
error = hn_synth_attach(sc, ETHERMTU); | error = hn_synth_attach(sc, ETHERMTU); | ||||
if (error) | if (error) | ||||
goto failed; | goto failed; | ||||
error = hn_rndis_get_linkstatus(sc, &link_status); | |||||
if (error) | |||||
goto failed; | |||||
if (link_status == NDIS_MEDIA_STATE_CONNECTED) | |||||
sc->hn_carrier = 1; | |||||
error = hn_rndis_get_eaddr(sc, eaddr); | |||||
if (error) | |||||
goto failed; | |||||
#if __FreeBSD_version >= 1100099 | |||||
if (sc->hn_rx_ring_inuse > 1) { | |||||
/* | |||||
* Reduce TCP segment aggregation limit for multiple | |||||
* RX rings to increase ACK timeliness. | |||||
*/ | |||||
hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF); | |||||
} | |||||
#endif | |||||
hn_set_chim_size(sc, sc->hn_chim_szmax); | |||||
if (hn_tx_chimney_size > 0 && | |||||
hn_tx_chimney_size < sc->hn_chim_szmax) | |||||
hn_set_chim_size(sc, hn_tx_chimney_size); | |||||
ctx = device_get_sysctl_ctx(dev); | |||||
child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | |||||
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "nvs_version", CTLFLAG_RD, | |||||
&sc->hn_nvs_ver, 0, "NVS version"); | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ndis_version", | |||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, | |||||
hn_ndis_version_sysctl, "A", "NDIS version"); | |||||
/* | |||||
* Setup the ifmedia, which has been initialized earlier. | |||||
*/ | |||||
ifmedia_add(&sc->hn_media, IFM_ETHER | IFM_AUTO, 0, NULL); | |||||
ifmedia_set(&sc->hn_media, IFM_ETHER | IFM_AUTO); | |||||
/* XXX ifmedia_set really should do this for us */ | |||||
sc->hn_media.ifm_media = sc->hn_media.ifm_cur->ifm_media; | |||||
/* | |||||
* Setup the ifnet for this interface. | |||||
*/ | |||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | ||||
ifp->if_ioctl = hn_ioctl; | ifp->if_ioctl = hn_ioctl; | ||||
ifp->if_init = hn_init; | ifp->if_init = hn_init; | ||||
if (hn_use_if_start) { | if (hn_use_if_start) { | ||||
int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]); | int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]); | ||||
ifp->if_start = hn_start; | ifp->if_start = hn_start; | ||||
IFQ_SET_MAXLEN(&ifp->if_snd, qdepth); | IFQ_SET_MAXLEN(&ifp->if_snd, qdepth); | ||||
ifp->if_snd.ifq_drv_maxlen = qdepth - 1; | ifp->if_snd.ifq_drv_maxlen = qdepth - 1; | ||||
IFQ_SET_READY(&ifp->if_snd); | IFQ_SET_READY(&ifp->if_snd); | ||||
} else { | } else { | ||||
ifp->if_transmit = hn_transmit; | ifp->if_transmit = hn_transmit; | ||||
ifp->if_qflush = hn_xmit_qflush; | ifp->if_qflush = hn_xmit_qflush; | ||||
} | } | ||||
ifmedia_init(&sc->hn_media, 0, hn_ifmedia_upd, hn_ifmedia_sts); | |||||
ifmedia_add(&sc->hn_media, IFM_ETHER | IFM_AUTO, 0, NULL); | |||||
ifmedia_set(&sc->hn_media, IFM_ETHER | IFM_AUTO); | |||||
/* XXX ifmedia_set really should do this for us */ | |||||
sc->hn_media.ifm_media = sc->hn_media.ifm_cur->ifm_media; | |||||
/* | |||||
* Tell upper layers that we support full VLAN capability. | |||||
*/ | |||||
ifp->if_capabilities |= | ifp->if_capabilities |= | ||||
IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_TSO | | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_TSO | | ||||
IFCAP_LRO; | IFCAP_LRO; | ||||
ifp->if_capenable |= | ifp->if_capenable |= | ||||
IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_TSO | | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_TSO | | ||||
IFCAP_LRO; | IFCAP_LRO; | ||||
ifp->if_hwassist = sc->hn_tx_ring[0].hn_csum_assist | CSUM_TSO; | ifp->if_hwassist = sc->hn_tx_ring[0].hn_csum_assist | CSUM_TSO; | ||||
#if __FreeBSD_version >= 1100099 | |||||
if (sc->hn_rx_ring_inuse > 1) { | |||||
/* | |||||
* Reduce TCP segment aggregation limit for multiple | |||||
* RX rings to increase ACK timeliness. | |||||
*/ | |||||
hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF); | |||||
} | |||||
#endif | |||||
error = hn_rndis_get_linkstatus(sc, &link_status); | |||||
if (error) | |||||
goto failed; | |||||
if (link_status == NDIS_MEDIA_STATE_CONNECTED) | |||||
sc->hn_carrier = 1; | |||||
tso_maxlen = hn_tso_maxlen; | tso_maxlen = hn_tso_maxlen; | ||||
if (tso_maxlen <= 0 || tso_maxlen > IP_MAXPACKET) | if (tso_maxlen <= 0 || tso_maxlen > IP_MAXPACKET) | ||||
tso_maxlen = IP_MAXPACKET; | tso_maxlen = IP_MAXPACKET; | ||||
ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX; | ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX; | ||||
ifp->if_hw_tsomaxsegsize = PAGE_SIZE; | ifp->if_hw_tsomaxsegsize = PAGE_SIZE; | ||||
ifp->if_hw_tsomax = tso_maxlen - | ifp->if_hw_tsomax = tso_maxlen - | ||||
(ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN); | (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN); | ||||
error = hn_rndis_get_eaddr(sc, eaddr); | |||||
if (error) | |||||
goto failed; | |||||
ether_ifattach(ifp, eaddr); | ether_ifattach(ifp, eaddr); | ||||
if_printf(ifp, "TSO: %u/%u/%u\n", ifp->if_hw_tsomax, | if_printf(ifp, "TSO: %u/%u/%u\n", ifp->if_hw_tsomax, | ||||
ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize); | ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize); | ||||
/* Inform the upper layer about the long frame support. */ | /* Inform the upper layer about the long frame support. */ | ||||
ifp->if_hdrlen = sizeof(struct ether_vlan_header); | ifp->if_hdrlen = sizeof(struct ether_vlan_header); | ||||
hn_set_chim_size(sc, sc->hn_chim_szmax); | |||||
if (hn_tx_chimney_size > 0 && | |||||
hn_tx_chimney_size < sc->hn_chim_szmax) | |||||
hn_set_chim_size(sc, hn_tx_chimney_size); | |||||
ctx = device_get_sysctl_ctx(dev); | |||||
child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | |||||
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "nvs_version", CTLFLAG_RD, | |||||
&sc->hn_nvs_ver, 0, "NVS version"); | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ndis_version", | |||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, | |||||
hn_ndis_version_sysctl, "A", "NDIS version"); | |||||
return (0); | return (0); | ||||
failed: | failed: | ||||
/* TODO: reuse netvsc_detach() */ | |||||
hn_destroy_tx_data(sc); | hn_destroy_tx_data(sc); | ||||
if (ifp != NULL) | if (ifp != NULL) | ||||
if_free(ifp); | if_free(ifp); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Standard detach entry point | * Standard detach entry point | ||||
▲ Show 20 Lines • Show All 2,807 Lines • Show Last 20 Lines |