Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hyperv/netvsc/hv_net_vsc.c
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver"); | MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver"); | ||||
/* | /* | ||||
* Forward declarations | * Forward declarations | ||||
*/ | */ | ||||
static void hv_nv_on_channel_callback(struct vmbus_channel *chan, | static void hv_nv_on_channel_callback(struct vmbus_channel *chan, | ||||
void *xrxr); | void *xrxr); | ||||
static int hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc); | static int hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc); | ||||
static int hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *); | static int hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *, int); | ||||
static int hv_nv_destroy_send_buffer(netvsc_dev *net_dev); | static int hv_nv_destroy_send_buffer(netvsc_dev *net_dev); | ||||
static int hv_nv_destroy_rx_buffer(netvsc_dev *net_dev); | static int hv_nv_destroy_rx_buffer(struct hn_softc *sc); | ||||
static int hv_nv_connect_to_vsp(struct hn_softc *sc); | static int hv_nv_connect_to_vsp(struct hn_softc *sc); | ||||
static void hv_nv_on_send_completion(netvsc_dev *net_dev, | static void hv_nv_on_send_completion(netvsc_dev *net_dev, | ||||
struct vmbus_channel *, const struct vmbus_chanpkt_hdr *pkt); | struct vmbus_channel *, const struct vmbus_chanpkt_hdr *pkt); | ||||
static void hv_nv_on_receive_completion(struct vmbus_channel *chan, | static void hv_nv_on_receive_completion(struct vmbus_channel *chan, | ||||
uint64_t tid); | uint64_t tid); | ||||
static void hv_nv_on_receive(netvsc_dev *net_dev, | static void hv_nv_on_receive(netvsc_dev *net_dev, | ||||
struct hn_rx_ring *rxr, struct vmbus_channel *chan, | struct hn_rx_ring *rxr, struct vmbus_channel *chan, | ||||
const struct vmbus_chanpkt_hdr *pkt); | const struct vmbus_chanpkt_hdr *pkt); | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Net VSC initialize receive buffer with net VSP | * Net VSC initialize receive buffer with net VSP | ||||
* | * | ||||
* Net VSP: Network virtual services client, also known as the | * Net VSP: Network virtual services client, also known as the | ||||
* Hyper-V extensible switch and the synthetic data path. | * Hyper-V extensible switch and the synthetic data path. | ||||
*/ | */ | ||||
static int | static int | ||||
hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc) | hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc, int rxbuf_size) | ||||
{ | { | ||||
struct vmbus_xact *xact; | struct vmbus_xact *xact; | ||||
struct hn_nvs_rxbuf_conn *conn; | struct hn_nvs_rxbuf_conn *conn; | ||||
const struct hn_nvs_rxbuf_connresp *resp; | const struct hn_nvs_rxbuf_connresp *resp; | ||||
size_t resp_len; | size_t resp_len; | ||||
struct hn_send_ctx sndc; | struct hn_send_ctx sndc; | ||||
netvsc_dev *net_dev; | |||||
uint32_t status; | uint32_t status; | ||||
int error; | int error; | ||||
net_dev = hv_nv_get_outbound_net_device(sc); | KASSERT(rxbuf_size <= NETVSC_RECEIVE_BUFFER_SIZE, | ||||
if (!net_dev) { | ("invalid rxbuf size %d", rxbuf_size)); | ||||
return (ENODEV); | |||||
} | |||||
net_dev->rx_buf = hyperv_dmamem_alloc(bus_get_dma_tag(sc->hn_dev), | |||||
PAGE_SIZE, 0, net_dev->rx_buf_size, &net_dev->rxbuf_dma, | |||||
BUS_DMA_WAITOK | BUS_DMA_ZERO); | |||||
if (net_dev->rx_buf == NULL) { | |||||
device_printf(sc->hn_dev, "allocate rxbuf failed\n"); | |||||
return (ENOMEM); | |||||
} | |||||
/* | /* | ||||
* Connect the RXBUF GPADL to the primary channel. | * Connect the RXBUF GPADL to the primary channel. | ||||
* | * | ||||
* NOTE: | * NOTE: | ||||
* Only primary channel has RXBUF connected to it. Sub-channels | * Only primary channel has RXBUF connected to it. Sub-channels | ||||
* just share this RXBUF. | * just share this RXBUF. | ||||
*/ | */ | ||||
error = vmbus_chan_gpadl_connect(sc->hn_prichan, | error = vmbus_chan_gpadl_connect(sc->hn_prichan, | ||||
net_dev->rxbuf_dma.hv_paddr, net_dev->rx_buf_size, | sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl); | ||||
&net_dev->rx_buf_gpadl_handle); | |||||
if (error) { | if (error) { | ||||
if_printf(sc->hn_ifp, "rxbuf gpadl connect failed: %d\n", | if_printf(sc->hn_ifp, "rxbuf gpadl connect failed: %d\n", | ||||
error); | error); | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
/* | /* | ||||
* Connect RXBUF to NVS. | * Connect RXBUF to NVS. | ||||
*/ | */ | ||||
xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn)); | xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn)); | ||||
if (xact == NULL) { | if (xact == NULL) { | ||||
if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n"); | if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n"); | ||||
error = ENXIO; | error = ENXIO; | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
conn = vmbus_xact_req_data(xact); | conn = vmbus_xact_req_data(xact); | ||||
conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN; | conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN; | ||||
conn->nvs_gpadl = net_dev->rx_buf_gpadl_handle; | conn->nvs_gpadl = sc->hn_rxbuf_gpadl; | ||||
conn->nvs_sig = HN_NVS_RXBUF_SIG; | conn->nvs_sig = HN_NVS_RXBUF_SIG; | ||||
hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact); | hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact); | ||||
vmbus_xact_activate(xact); | vmbus_xact_activate(xact); | ||||
error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC, | error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC, | ||||
conn, sizeof(*conn), &sndc); | conn, sizeof(*conn), &sndc); | ||||
if (error != 0) { | if (error != 0) { | ||||
Show All 23 Lines | hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc, int rxbuf_size) | ||||
status = resp->nvs_status; | status = resp->nvs_status; | ||||
vmbus_xact_put(xact); | vmbus_xact_put(xact); | ||||
if (status != HN_NVS_STATUS_OK) { | if (status != HN_NVS_STATUS_OK) { | ||||
if_printf(sc->hn_ifp, "rxbuf conn failed: %x\n", status); | if_printf(sc->hn_ifp, "rxbuf conn failed: %x\n", status); | ||||
error = EIO; | error = EIO; | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
net_dev->rx_section_count = 1; | sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED; | ||||
return (0); | return (0); | ||||
cleanup: | cleanup: | ||||
hv_nv_destroy_rx_buffer(net_dev); | hv_nv_destroy_rx_buffer(sc); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Net VSC initialize send buffer with net VSP | * Net VSC initialize send buffer with net VSP | ||||
*/ | */ | ||||
static int | static int | ||||
hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc) | hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc) | ||||
▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | cleanup: | ||||
hv_nv_destroy_send_buffer(net_dev); | hv_nv_destroy_send_buffer(net_dev); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Net VSC destroy receive buffer | * Net VSC destroy receive buffer | ||||
*/ | */ | ||||
static int | static int | ||||
hv_nv_destroy_rx_buffer(netvsc_dev *net_dev) | hv_nv_destroy_rx_buffer(struct hn_softc *sc) | ||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
if (net_dev->rx_section_count) { | if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) { | ||||
struct hn_nvs_rxbuf_disconn disconn; | struct hn_nvs_rxbuf_disconn disconn; | ||||
/* | /* | ||||
* Disconnect RXBUF from NVS. | * Disconnect RXBUF from NVS. | ||||
*/ | */ | ||||
memset(&disconn, 0, sizeof(disconn)); | memset(&disconn, 0, sizeof(disconn)); | ||||
disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN; | disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN; | ||||
disconn.nvs_sig = HN_NVS_RXBUF_SIG; | disconn.nvs_sig = HN_NVS_RXBUF_SIG; | ||||
/* NOTE: No response. */ | /* NOTE: No response. */ | ||||
ret = hn_nvs_send(net_dev->sc->hn_prichan, | ret = hn_nvs_send(sc->hn_prichan, | ||||
VMBUS_CHANPKT_FLAG_NONE, &disconn, sizeof(disconn), | VMBUS_CHANPKT_FLAG_NONE, &disconn, sizeof(disconn), | ||||
&hn_send_ctx_none); | &hn_send_ctx_none); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
if_printf(net_dev->sc->hn_ifp, | if_printf(sc->hn_ifp, | ||||
"send rxbuf disconn failed: %d\n", ret); | "send rxbuf disconn failed: %d\n", ret); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
net_dev->rx_section_count = 0; | sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED; | ||||
} | } | ||||
/* Tear down the gpadl on the vsp end */ | if (sc->hn_rxbuf_gpadl != 0) { | ||||
if (net_dev->rx_buf_gpadl_handle) { | |||||
ret = vmbus_chan_gpadl_disconnect(net_dev->sc->hn_prichan, | |||||
net_dev->rx_buf_gpadl_handle); | |||||
/* | /* | ||||
* If we failed here, we might as well return and have a leak | * Disconnect RXBUF from primary channel. | ||||
* rather than continue and a bugchk | |||||
*/ | */ | ||||
ret = vmbus_chan_gpadl_disconnect(sc->hn_prichan, | |||||
sc->hn_rxbuf_gpadl); | |||||
if (ret != 0) { | if (ret != 0) { | ||||
if_printf(sc->hn_ifp, | |||||
"rxbuf disconn failed: %d\n", ret); | |||||
return (ret); | return (ret); | ||||
} | } | ||||
net_dev->rx_buf_gpadl_handle = 0; | sc->hn_rxbuf_gpadl = 0; | ||||
} | } | ||||
if (net_dev->rx_buf) { | |||||
/* Free up the receive buffer */ | |||||
hyperv_dmamem_free(&net_dev->rxbuf_dma, net_dev->rx_buf); | |||||
net_dev->rx_buf = NULL; | |||||
} | |||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* Net VSC destroy send buffer | * Net VSC destroy send buffer | ||||
*/ | */ | ||||
static int | static int | ||||
hv_nv_destroy_send_buffer(netvsc_dev *net_dev) | hv_nv_destroy_send_buffer(netvsc_dev *net_dev) | ||||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | uint32_t protocol_list[] = { NVSP_PROTOCOL_VERSION_1, | ||||
NVSP_PROTOCOL_VERSION_4, | NVSP_PROTOCOL_VERSION_4, | ||||
NVSP_PROTOCOL_VERSION_5 }; | NVSP_PROTOCOL_VERSION_5 }; | ||||
int i; | int i; | ||||
int protocol_number = nitems(protocol_list); | int protocol_number = nitems(protocol_list); | ||||
int ret = 0; | int ret = 0; | ||||
device_t dev = sc->hn_dev; | device_t dev = sc->hn_dev; | ||||
struct ifnet *ifp = sc->hn_ifp; | struct ifnet *ifp = sc->hn_ifp; | ||||
struct hn_nvs_ndis_init ndis; | struct hn_nvs_ndis_init ndis; | ||||
int rxbuf_size; | |||||
net_dev = hv_nv_get_outbound_net_device(sc); | net_dev = hv_nv_get_outbound_net_device(sc); | ||||
/* | /* | ||||
* Negotiate the NVSP version. Try the latest NVSP first. | * Negotiate the NVSP version. Try the latest NVSP first. | ||||
*/ | */ | ||||
for (i = protocol_number - 1; i >= 0; i--) { | for (i = protocol_number - 1; i >= 0; i--) { | ||||
if (hv_nv_negotiate_nvsp_protocol(sc, net_dev, | if (hv_nv_negotiate_nvsp_protocol(sc, net_dev, | ||||
Show All 38 Lines | ret = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE, | ||||
&ndis, sizeof(ndis), &hn_send_ctx_none); | &ndis, sizeof(ndis), &hn_send_ctx_none); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", ret); | if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", ret); | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
/* Post the big receive buffer to NetVSP */ | /* Post the big receive buffer to NetVSP */ | ||||
if (sc->hn_nvs_ver <= NVSP_PROTOCOL_VERSION_2) | if (sc->hn_nvs_ver <= NVSP_PROTOCOL_VERSION_2) | ||||
net_dev->rx_buf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY; | rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY; | ||||
else | else | ||||
net_dev->rx_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; | rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE; | ||||
net_dev->send_buf_size = NETVSC_SEND_BUFFER_SIZE; | net_dev->send_buf_size = NETVSC_SEND_BUFFER_SIZE; | ||||
ret = hv_nv_init_rx_buffer_with_net_vsp(sc); | ret = hv_nv_init_rx_buffer_with_net_vsp(sc, rxbuf_size); | ||||
if (ret == 0) | if (ret == 0) | ||||
ret = hv_nv_init_send_buffer_with_net_vsp(sc); | ret = hv_nv_init_send_buffer_with_net_vsp(sc); | ||||
cleanup: | cleanup: | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* Net VSC disconnect from VSP | * Net VSC disconnect from VSP | ||||
*/ | */ | ||||
static void | static void | ||||
hv_nv_disconnect_from_vsp(netvsc_dev *net_dev) | hv_nv_disconnect_from_vsp(struct hn_softc *sc) | ||||
{ | { | ||||
hv_nv_destroy_rx_buffer(net_dev); | hv_nv_destroy_rx_buffer(sc); | ||||
hv_nv_destroy_send_buffer(net_dev); | hv_nv_destroy_send_buffer(sc->net_dev); | ||||
} | } | ||||
void | void | ||||
hv_nv_subchan_attach(struct vmbus_channel *chan, struct hn_rx_ring *rxr) | hv_nv_subchan_attach(struct vmbus_channel *chan, struct hn_rx_ring *rxr) | ||||
{ | { | ||||
KASSERT(rxr->hn_rx_idx == vmbus_chan_subidx(chan), | KASSERT(rxr->hn_rx_idx == vmbus_chan_subidx(chan), | ||||
("chan%u subidx %u, rxr%d mismatch", | ("chan%u subidx %u, rxr%d mismatch", | ||||
vmbus_chan_id(chan), vmbus_chan_subidx(chan), rxr->hn_rx_idx)); | vmbus_chan_id(chan), vmbus_chan_subidx(chan), rxr->hn_rx_idx)); | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Net VSC on device remove | * Net VSC on device remove | ||||
*/ | */ | ||||
int | int | ||||
hv_nv_on_device_remove(struct hn_softc *sc, boolean_t destroy_channel) | hv_nv_on_device_remove(struct hn_softc *sc, boolean_t destroy_channel) | ||||
{ | { | ||||
netvsc_dev *net_dev = sc->net_dev;; | |||||
hv_nv_disconnect_from_vsp(net_dev); | hv_nv_disconnect_from_vsp(sc); | ||||
/* At this point, no one should be accessing net_dev except in here */ | |||||
/* Now, we can close the channel safely */ | /* Now, we can close the channel safely */ | ||||
vmbus_chan_close(sc->hn_prichan); | vmbus_chan_close(sc->hn_prichan); | ||||
free(net_dev, M_NETVSC); | free(sc->net_dev, M_NETVSC); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
hn_nvs_sent_xact(struct hn_send_ctx *sndc, | hn_nvs_sent_xact(struct hn_send_ctx *sndc, | ||||
struct netvsc_dev_ *net_dev __unused, struct vmbus_channel *chan __unused, | struct netvsc_dev_ *net_dev __unused, struct vmbus_channel *chan __unused, | ||||
const void *data, int dlen) | const void *data, int dlen) | ||||
▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Lines | if (pkt->cp_rxbuf_id != NETVSC_RECEIVE_BUFFER_ID) { | ||||
return; | return; | ||||
} | } | ||||
count = pkt->cp_rxbuf_cnt; | count = pkt->cp_rxbuf_cnt; | ||||
/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */ | /* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */ | ||||
for (i = 0; i < count; i++) { | for (i = 0; i < count; i++) { | ||||
hv_rf_on_receive(net_dev, rxr, | hv_rf_on_receive(net_dev, rxr, | ||||
(const uint8_t *)net_dev->rx_buf + pkt->cp_rxbuf[i].rb_ofs, | rxr->hn_rxbuf + pkt->cp_rxbuf[i].rb_ofs, | ||||
pkt->cp_rxbuf[i].rb_len); | pkt->cp_rxbuf[i].rb_len); | ||||
} | } | ||||
/* | /* | ||||
* Moved completion call back here so that all received | * Moved completion call back here so that all received | ||||
* messages (not just data messages) will trigger a response | * messages (not just data messages) will trigger a response | ||||
* message back to the host. | * message back to the host. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 121 Lines • Show Last 20 Lines |