Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hyperv/netvsc/if_hn.c
Show First 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
#include <sys/smp.h> | #include <sys/smp.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/sockio.h> | #include <sys/sockio.h> | ||||
#include <sys/sx.h> | #include <sys/sx.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/taskqueue.h> | #include <sys/taskqueue.h> | ||||
#include <sys/buf_ring.h> | #include <sys/buf_ring.h> | ||||
#include <sys/eventhandler.h> | |||||
#include <machine/atomic.h> | #include <machine/atomic.h> | ||||
#include <machine/in_cksum.h> | #include <machine/in_cksum.h> | ||||
#include <net/bpf.h> | #include <net/bpf.h> | ||||
#include <net/ethernet.h> | #include <net/ethernet.h> | ||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_dl.h> | |||||
#include <net/if_media.h> | #include <net/if_media.h> | ||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/rndis.h> | #include <net/rndis.h> | ||||
#ifdef RSS | #ifdef RSS | ||||
#include <net/rss_config.h> | #include <net/rss_config.h> | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | |||||
struct hn_rxinfo { | struct hn_rxinfo { | ||||
uint32_t vlan_info; | uint32_t vlan_info; | ||||
uint32_t csum_info; | uint32_t csum_info; | ||||
uint32_t hash_info; | uint32_t hash_info; | ||||
uint32_t hash_value; | uint32_t hash_value; | ||||
}; | }; | ||||
struct hn_update_vf { | |||||
struct hn_rx_ring *rxr; | |||||
struct ifnet *vf; | |||||
}; | |||||
#define HN_RXINFO_VLAN 0x0001 | #define HN_RXINFO_VLAN 0x0001 | ||||
#define HN_RXINFO_CSUM 0x0002 | #define HN_RXINFO_CSUM 0x0002 | ||||
#define HN_RXINFO_HASHINF 0x0004 | #define HN_RXINFO_HASHINF 0x0004 | ||||
#define HN_RXINFO_HASHVAL 0x0008 | #define HN_RXINFO_HASHVAL 0x0008 | ||||
#define HN_RXINFO_ALL \ | #define HN_RXINFO_ALL \ | ||||
(HN_RXINFO_VLAN | \ | (HN_RXINFO_VLAN | \ | ||||
HN_RXINFO_CSUM | \ | HN_RXINFO_CSUM | \ | ||||
HN_RXINFO_HASHINF | \ | HN_RXINFO_HASHINF | \ | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
static int hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS); | static int hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS); | ||||
static int hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS); | static int hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS); | ||||
static int hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS); | static int hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS); | ||||
static int hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS); | static int hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS); | ||||
static int hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS); | static int hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS); | ||||
static int hn_polling_sysctl(SYSCTL_HANDLER_ARGS); | static int hn_polling_sysctl(SYSCTL_HANDLER_ARGS); | ||||
static void hn_stop(struct hn_softc *); | static void hn_stop(struct hn_softc *, bool); | ||||
static void hn_init_locked(struct hn_softc *); | static void hn_init_locked(struct hn_softc *); | ||||
static int hn_chan_attach(struct hn_softc *, | static int hn_chan_attach(struct hn_softc *, | ||||
struct vmbus_channel *); | struct vmbus_channel *); | ||||
static void hn_chan_detach(struct hn_softc *, | static void hn_chan_detach(struct hn_softc *, | ||||
struct vmbus_channel *); | struct vmbus_channel *); | ||||
static int hn_attach_subchans(struct hn_softc *); | static int hn_attach_subchans(struct hn_softc *); | ||||
static void hn_detach_allchans(struct hn_softc *); | static void hn_detach_allchans(struct hn_softc *); | ||||
static void hn_chan_rollup(struct hn_rx_ring *, | static void hn_chan_rollup(struct hn_rx_ring *, | ||||
▲ Show 20 Lines • Show All 395 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
hn_rxfilter_config(struct hn_softc *sc) | hn_rxfilter_config(struct hn_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->hn_ifp; | struct ifnet *ifp = sc->hn_ifp; | ||||
uint32_t filter; | uint32_t filter; | ||||
HN_LOCK_ASSERT(sc); | HN_LOCK_ASSERT(sc); | ||||
if (ifp->if_flags & IFF_PROMISC) { | if ((ifp->if_flags & IFF_PROMISC) || | ||||
(sc->hn_flags & HN_FLAG_VF)) { | |||||
filter = NDIS_PACKET_TYPE_PROMISCUOUS; | filter = NDIS_PACKET_TYPE_PROMISCUOUS; | ||||
} else { | } else { | ||||
filter = NDIS_PACKET_TYPE_DIRECTED; | filter = NDIS_PACKET_TYPE_DIRECTED; | ||||
if (ifp->if_flags & IFF_BROADCAST) | if (ifp->if_flags & IFF_BROADCAST) | ||||
filter |= NDIS_PACKET_TYPE_BROADCAST; | filter |= NDIS_PACKET_TYPE_BROADCAST; | ||||
/* TODO: support multicast list */ | /* TODO: support multicast list */ | ||||
if ((ifp->if_flags & IFF_ALLMULTI) || | if ((ifp->if_flags & IFF_ALLMULTI) || | ||||
!TAILQ_EMPTY(&ifp->if_multiaddrs)) | !TAILQ_EMPTY(&ifp->if_multiaddrs)) | ||||
▲ Show 20 Lines • Show All 172 Lines • ▼ Show 20 Lines | hn_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) | ||||
if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) { | if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) { | ||||
ifmr->ifm_active |= IFM_NONE; | ifmr->ifm_active |= IFM_NONE; | ||||
return; | return; | ||||
} | } | ||||
ifmr->ifm_status |= IFM_ACTIVE; | ifmr->ifm_status |= IFM_ACTIVE; | ||||
ifmr->ifm_active |= IFM_10G_T | IFM_FDX; | ifmr->ifm_active |= IFM_10G_T | IFM_FDX; | ||||
} | } | ||||
static void | |||||
hn_update_vf_task(void *arg, int pending __unused) | |||||
{ | |||||
struct hn_update_vf *uv = arg; | |||||
uv->rxr->hn_vf = uv->vf; | |||||
} | |||||
static void | |||||
hn_update_vf(struct hn_softc *sc, struct ifnet *vf) | |||||
{ | |||||
struct hn_rx_ring *rxr; | |||||
struct hn_update_vf uv; | |||||
struct task task; | |||||
int i; | |||||
HN_LOCK_ASSERT(sc); | |||||
TASK_INIT(&task, 0, hn_update_vf_task, &uv); | |||||
for (i = 0; i < sc->hn_rx_ring_cnt; ++i) { | |||||
rxr = &sc->hn_rx_ring[i]; | |||||
if (i < sc->hn_rx_ring_inuse) { | |||||
uv.rxr = rxr; | |||||
uv.vf = vf; | |||||
vmbus_chan_run_task(rxr->hn_chan, &task); | |||||
} else { | |||||
rxr->hn_vf = vf; | |||||
} | |||||
} | |||||
} | |||||
static void | |||||
hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf) | |||||
{ | |||||
struct ifnet *hn_ifp; | |||||
HN_LOCK(sc); | |||||
if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)) | |||||
goto out; | |||||
hn_ifp = sc->hn_ifp; | |||||
if (ifp == hn_ifp) | |||||
goto out; | |||||
if (ifp->if_alloctype != IFT_ETHER) | |||||
goto out; | |||||
/* Ignore lagg/vlan interfaces */ | |||||
if (strcmp(ifp->if_dname, "lagg") == 0 || | |||||
strcmp(ifp->if_dname, "vlan") == 0) | |||||
goto out; | |||||
if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0) | |||||
goto out; | |||||
/* Now we're sure 'ifp' is a real VF device. */ | |||||
if (vf) { | |||||
if (sc->hn_flags & HN_FLAG_VF) | |||||
goto out; | |||||
sc->hn_flags |= HN_FLAG_VF; | |||||
hn_rxfilter_config(sc); | |||||
} else { | |||||
if (!(sc->hn_flags & HN_FLAG_VF)) | |||||
goto out; | |||||
sc->hn_flags &= ~HN_FLAG_VF; | |||||
if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) | |||||
hn_rxfilter_config(sc); | |||||
else | |||||
hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE); | |||||
} | |||||
hn_nvs_set_datapath(sc, | |||||
vf ? HN_NVS_DATAPATH_VF : HN_NVS_DATAPATH_SYNTHETIC); | |||||
hn_update_vf(sc, vf ? ifp : NULL); | |||||
if (vf) { | |||||
hn_suspend_mgmt(sc); | |||||
sc->hn_link_flags &= | |||||
~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG); | |||||
if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN); | |||||
} else { | |||||
hn_resume_mgmt(sc); | |||||
} | |||||
if (bootverbose) | |||||
if_printf(hn_ifp, "Data path is switched %s %s\n", | |||||
vf ? "to" : "from", if_name(ifp)); | |||||
out: | |||||
HN_UNLOCK(sc); | |||||
} | |||||
static void | |||||
hn_ifnet_event(void *arg, struct ifnet *ifp, int event) | |||||
{ | |||||
if (event != IFNET_EVENT_UP && event != IFNET_EVENT_DOWN) | |||||
return; | |||||
hn_set_vf(arg, ifp, event == IFNET_EVENT_UP); | |||||
} | |||||
static void | |||||
hn_ifaddr_event(void *arg, struct ifnet *ifp) | |||||
{ | |||||
hn_set_vf(arg, ifp, ifp->if_flags & IFF_UP); | |||||
} | |||||
/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ | /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ | ||||
static const struct hyperv_guid g_net_vsc_device_type = { | static const struct hyperv_guid g_net_vsc_device_type = { | ||||
.hv_guid = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, | .hv_guid = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, | ||||
0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E} | 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E} | ||||
}; | }; | ||||
static int | static int | ||||
hn_probe(device_t dev) | hn_probe(device_t dev) | ||||
▲ Show 20 Lines • Show All 309 Lines • ▼ Show 20 Lines | #endif | ||||
ifp->if_hdrlen = sizeof(struct ether_vlan_header); | ifp->if_hdrlen = sizeof(struct ether_vlan_header); | ||||
/* | /* | ||||
* Kick off link status check. | * Kick off link status check. | ||||
*/ | */ | ||||
sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0; | sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0; | ||||
hn_update_link_status(sc); | hn_update_link_status(sc); | ||||
sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event, | |||||
hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY); | |||||
sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event, | |||||
hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY); | |||||
return (0); | return (0); | ||||
failed: | failed: | ||||
if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) | if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) | ||||
hn_synth_detach(sc); | hn_synth_detach(sc); | ||||
hn_detach(dev); | hn_detach(dev); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
hn_detach(device_t dev) | hn_detach(device_t dev) | ||||
{ | { | ||||
struct hn_softc *sc = device_get_softc(dev); | struct hn_softc *sc = device_get_softc(dev); | ||||
struct ifnet *ifp = sc->hn_ifp; | struct ifnet *ifp = sc->hn_ifp; | ||||
if (sc->hn_ifaddr_evthand != NULL) | |||||
EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand); | |||||
if (sc->hn_ifnet_evthand != NULL) | |||||
EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand); | |||||
if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) { | if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) { | ||||
/* | /* | ||||
* In case that the vmbus missed the orphan handler | * In case that the vmbus missed the orphan handler | ||||
* installation. | * installation. | ||||
*/ | */ | ||||
vmbus_xact_ctx_orphan(sc->hn_xact); | vmbus_xact_ctx_orphan(sc->hn_xact); | ||||
} | } | ||||
if (device_is_attached(dev)) { | if (device_is_attached(dev)) { | ||||
HN_LOCK(sc); | HN_LOCK(sc); | ||||
if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) { | if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) { | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) | if (ifp->if_drv_flags & IFF_DRV_RUNNING) | ||||
hn_stop(sc); | hn_stop(sc, true); | ||||
/* | /* | ||||
* NOTE: | * NOTE: | ||||
* hn_stop() only suspends data, so managment | * hn_stop() only suspends data, so managment | ||||
* stuffs have to be suspended manually here. | * stuffs have to be suspended manually here. | ||||
*/ | */ | ||||
hn_suspend_mgmt(sc); | hn_suspend_mgmt(sc); | ||||
hn_synth_detach(sc); | hn_synth_detach(sc); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 860 Lines • ▼ Show 20 Lines | #endif | ||||
return tcp_lro_rx(lc, m, 0); | return tcp_lro_rx(lc, m, 0); | ||||
} | } | ||||
#endif | #endif | ||||
static int | static int | ||||
hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen, | hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen, | ||||
const struct hn_rxinfo *info) | const struct hn_rxinfo *info) | ||||
{ | { | ||||
struct ifnet *ifp = rxr->hn_ifp; | struct ifnet *ifp; | ||||
struct mbuf *m_new; | struct mbuf *m_new; | ||||
int size, do_lro = 0, do_csum = 1; | int size, do_lro = 0, do_csum = 1; | ||||
int hash_type; | int hash_type; | ||||
/* If the VF is active, inject the packet through the VF */ | |||||
ifp = rxr->hn_vf ? rxr->hn_vf : rxr->hn_ifp; | |||||
if (dlen <= MHLEN) { | if (dlen <= MHLEN) { | ||||
m_new = m_gethdr(M_NOWAIT, MT_DATA); | m_new = m_gethdr(M_NOWAIT, MT_DATA); | ||||
if (m_new == NULL) { | if (m_new == NULL) { | ||||
if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); | if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); | ||||
return (0); | return (0); | ||||
} | } | ||||
memcpy(mtod(m_new, void *), data, dlen); | memcpy(mtod(m_new, void *), data, dlen); | ||||
m_new->m_pkthdr.len = m_new->m_len = dlen; | m_new->m_pkthdr.len = m_new->m_len = dlen; | ||||
▲ Show 20 Lines • Show All 294 Lines • ▼ Show 20 Lines | if (ifp->if_flags & IFF_UP) { | ||||
HN_NO_SLEEPING(sc); | HN_NO_SLEEPING(sc); | ||||
hn_rxfilter_config(sc); | hn_rxfilter_config(sc); | ||||
HN_SLEEPING_OK(sc); | HN_SLEEPING_OK(sc); | ||||
} else { | } else { | ||||
hn_init_locked(sc); | hn_init_locked(sc); | ||||
} | } | ||||
} else { | } else { | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) | if (ifp->if_drv_flags & IFF_DRV_RUNNING) | ||||
hn_stop(sc); | hn_stop(sc, false); | ||||
} | } | ||||
sc->hn_if_flags = ifp->if_flags; | sc->hn_if_flags = ifp->if_flags; | ||||
HN_UNLOCK(sc); | HN_UNLOCK(sc); | ||||
break; | break; | ||||
case SIOCSIFCAP: | case SIOCSIFCAP: | ||||
HN_LOCK(sc); | HN_LOCK(sc); | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | #endif | ||||
default: | default: | ||||
error = ether_ioctl(ifp, cmd, data); | error = ether_ioctl(ifp, cmd, data); | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
hn_stop(struct hn_softc *sc) | hn_stop(struct hn_softc *sc, bool detaching) | ||||
{ | { | ||||
struct ifnet *ifp = sc->hn_ifp; | struct ifnet *ifp = sc->hn_ifp; | ||||
int i; | int i; | ||||
HN_LOCK_ASSERT(sc); | HN_LOCK_ASSERT(sc); | ||||
KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED, | KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED, | ||||
("synthetic parts were not attached")); | ("synthetic parts were not attached")); | ||||
/* Disable polling. */ | /* Disable polling. */ | ||||
hn_polling(sc, 0); | hn_polling(sc, 0); | ||||
/* Clear RUNNING bit _before_ hn_suspend_data() */ | /* Clear RUNNING bit _before_ hn_suspend_data() */ | ||||
atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING); | atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING); | ||||
hn_suspend_data(sc); | hn_suspend_data(sc); | ||||
/* Clear OACTIVE bit. */ | /* Clear OACTIVE bit. */ | ||||
atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE); | atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE); | ||||
for (i = 0; i < sc->hn_tx_ring_inuse; ++i) | for (i = 0; i < sc->hn_tx_ring_inuse; ++i) | ||||
sc->hn_tx_ring[i].hn_oactive = 0; | sc->hn_tx_ring[i].hn_oactive = 0; | ||||
/* | |||||
* If the VF is active, make sure the filter is not 0, even if | |||||
* the synthetic NIC is down. | |||||
*/ | |||||
if (!detaching && (sc->hn_flags & HN_FLAG_VF)) | |||||
hn_rxfilter_config(sc); | |||||
} | } | ||||
static void | static void | ||||
hn_init_locked(struct hn_softc *sc) | hn_init_locked(struct hn_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp = sc->hn_ifp; | struct ifnet *ifp = sc->hn_ifp; | ||||
int i; | int i; | ||||
▲ Show 20 Lines • Show All 2,328 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
hn_suspend(struct hn_softc *sc) | hn_suspend(struct hn_softc *sc) | ||||
{ | { | ||||
/* Disable polling. */ | /* Disable polling. */ | ||||
hn_polling(sc, 0); | hn_polling(sc, 0); | ||||
if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) | if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) || | ||||
(sc->hn_flags & HN_FLAG_VF)) | |||||
hn_suspend_data(sc); | hn_suspend_data(sc); | ||||
hn_suspend_mgmt(sc); | hn_suspend_mgmt(sc); | ||||
} | } | ||||
static void | static void | ||||
hn_resume_tx(struct hn_softc *sc, int tx_ring_cnt) | hn_resume_tx(struct hn_softc *sc, int tx_ring_cnt) | ||||
{ | { | ||||
int i; | int i; | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | hn_resume_mgmt(struct hn_softc *sc) | ||||
else | else | ||||
hn_update_link_status(sc); | hn_update_link_status(sc); | ||||
} | } | ||||
static void | static void | ||||
hn_resume(struct hn_softc *sc) | hn_resume(struct hn_softc *sc) | ||||
{ | { | ||||
if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) | if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) || | ||||
(sc->hn_flags & HN_FLAG_VF)) | |||||
hn_resume_data(sc); | hn_resume_data(sc); | ||||
/* | |||||
* When the VF is activated, the synthetic interface is changed | |||||
* to DOWN in hn_set_vf(). Here, if the VF is still active, we | |||||
* don't call hn_resume_mgmt() until the VF is deactivated in | |||||
* hn_set_vf(). | |||||
*/ | |||||
if (!(sc->hn_flags & HN_FLAG_VF)) | |||||
hn_resume_mgmt(sc); | hn_resume_mgmt(sc); | ||||
/* | /* | ||||
* Re-enable polling if this interface is running and | * Re-enable polling if this interface is running and | ||||
* the polling is requested. | * the polling is requested. | ||||
*/ | */ | ||||
if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->hn_pollhz > 0) | if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->hn_pollhz > 0) | ||||
hn_polling(sc, sc->hn_pollhz); | hn_polling(sc, sc->hn_pollhz); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 572 Lines • Show Last 20 Lines |