/* Worker to setup sub channels on initial setup *Initialhotplugeventoccursinsoftirqcontext *andcan'twaitforchannels.
*/ staticvoid netvsc_subchan_work(struct work_struct *w)
{ struct netvsc_device *nvdev =
container_of(w, struct netvsc_device, subchan_work); struct rndis_device *rdev; int i, ret;
/* Avoid deadlock with device removal already under RTNL */ if (!rtnl_trylock()) {
schedule_work(w); return;
}
rdev = nvdev->extension; if (rdev) {
ret = rndis_set_subchannel(rdev->ndev, nvdev, NULL); if (ret == 0) {
netif_device_attach(rdev->ndev);
} else { /* fallback to only primary channel */ for (i = 1; i < nvdev->num_chn; i++)
netif_napi_del(&nvdev->chan_table[i].napi);
if (!nvdev->recv_buf_gpadl_handle.decrypted)
vfree(nvdev->recv_buf); if (!nvdev->send_buf_gpadl_handle.decrypted)
vfree(nvdev->send_buf);
bitmap_free(nvdev->send_section_map);
for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
xdp_rxq_info_unreg(&nvdev->chan_table[i].xdp_rxq);
kfree(nvdev->chan_table[i].recv_buf);
vfree(nvdev->chan_table[i].mrc.slots);
}
ret = vmbus_sendpacket(device->channel,
revoke_packet, sizeof(struct nvsp_message),
VMBUS_RQST_ID_NO_RESPONSE,
VM_PKT_DATA_INBAND, 0); /* If the failure is because the channel is rescinded; *ignorethefailuresincewecannotsendonarescinded *channel.Thiswouldallowustoproperlycleanup *evenwhenthechannelisrescinded.
*/ if (device->channel->rescind)
ret = 0; /* *Ifwefailedhere,wemightaswellreturnand *havealeakratherthancontinueandabugchk
*/ if (ret != 0) {
netdev_err(ndev, "unable to send " "revoke receive buffer to netvsp\n"); return;
}
net_device->recv_section_cnt = 0;
}
}
/* Deal with the send buffer we may have setup. *Ifwegotasendsectionsize,itmeanswereceiveda *NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETEmsg(iesent *NVSP_MSG1_TYPE_SEND_SEND_BUFmsg)therefore,weneed *tosendarevokemsghere
*/ if (net_device->send_section_cnt) { /* Send the revoke receive buffer */
revoke_packet = &net_device->revoke_packet;
memset(revoke_packet, 0, sizeof(struct nvsp_message));
ret = vmbus_sendpacket(device->channel,
revoke_packet, sizeof(struct nvsp_message),
VMBUS_RQST_ID_NO_RESPONSE,
VM_PKT_DATA_INBAND, 0);
/* If the failure is because the channel is rescinded; *ignorethefailuresincewecannotsendonarescinded *channel.Thiswouldallowustoproperlycleanup *evenwhenthechannelisrescinded.
*/ if (device->channel->rescind)
ret = 0;
/* If we failed here, we might as well return and *havealeakratherthancontinueandabugchk
*/ if (ret != 0) {
netdev_err(ndev, "unable to send " "revoke send buffer to netvsp\n"); return;
}
net_device->send_section_cnt = 0;
}
}
if (net_device->recv_buf_gpadl_handle.gpadl_handle) {
ret = vmbus_teardown_gpadl(device->channel,
&net_device->recv_buf_gpadl_handle);
/* If we failed here, we might as well return and have a leak *ratherthancontinueandabugchk
*/ if (ret != 0) {
netdev_err(ndev, "unable to teardown receive buffer's gpadl\n"); return;
}
}
}
if (net_device->send_buf_gpadl_handle.gpadl_handle) {
ret = vmbus_teardown_gpadl(device->channel,
&net_device->send_buf_gpadl_handle);
/* If we failed here, we might as well return and have a leak *ratherthancontinueandabugchk
*/ if (ret != 0) {
netdev_err(ndev, "unable to teardown send buffer's gpadl\n"); return;
}
}
}
int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx)
{ struct netvsc_channel *nvchan = &net_device->chan_table[q_idx]; int node = cpu_to_node(nvchan->channel->target_cpu);
size_t size;
/* There should only be one section for the entire receive buffer */ if (resp->num_sections != 1 || resp->sections[0].offset != 0) {
ret = -EINVAL; goto cleanup;
}
/* Now setup the send buffer. */
buf_size = device_info->send_sections * device_info->send_section_size;
buf_size = round_up(buf_size, PAGE_SIZE);
net_device->send_buf = vzalloc(buf_size); if (!net_device->send_buf) {
netdev_err(ndev, "unable to allocate send buffer of size %u\n",
buf_size);
ret = -ENOMEM; goto cleanup;
}
net_device->send_buf_size = buf_size;
/* Establish the gpadl handle for this buffer on this *channel.Note:Thiscallusesthevmbusconnectionrather *thanthechanneltoestablishthegpadlhandle.
*/
ret = vmbus_establish_gpadl(device->channel, net_device->send_buf,
buf_size,
&net_device->send_buf_gpadl_handle); if (ret != 0) {
netdev_err(ndev, "unable to establish send buffer's gpadl\n"); goto cleanup;
}
/* Notify the NetVsp of the gpadl handle */
init_packet = &net_device->channel_init_pkt;
memset(init_packet, 0, sizeof(struct nvsp_message));
init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_SEND_BUF;
init_packet->msg.v1_msg.send_send_buf.gpadl_handle =
net_device->send_buf_gpadl_handle.gpadl_handle;
init_packet->msg.v1_msg.send_send_buf.id = NETVSC_SEND_BUFFER_ID;
trace_nvsp_send(ndev, init_packet);
/* Send the gpadl notification request */
ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message),
(unsignedlong)init_packet,
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) {
netdev_err(ndev, "unable to send send buffer's gpadl to netvsp\n"); goto cleanup;
}
/* Setup state for managing the send buffer. */
net_device->send_section_map = bitmap_zalloc(net_device->send_section_cnt,
GFP_KERNEL); if (!net_device->send_section_map) {
ret = -ENOMEM; goto cleanup;
}
if (nvsp_ver >= NVSP_PROTOCOL_VERSION_5) { if (hv_is_isolation_supported())
netdev_info(ndev, "SR-IOV not advertised by guests on the host supporting isolation\n"); else
init_packet->msg.v2_msg.send_ndis_config.capability.sriov = 1;
/* Teaming bit is needed to receive link speed updates */
init_packet->msg.v2_msg.send_ndis_config.capability.teaming = 1;
}
if (nvsp_ver >= NVSP_PROTOCOL_VERSION_61)
init_packet->msg.v2_msg.send_ndis_config.capability.rsc = 1;
trace_nvsp_send(ndev, init_packet);
ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message),
VMBUS_RQST_ID_NO_RESPONSE,
VM_PKT_DATA_INBAND, 0);
netvsc_revoke_send_buf(device, net_device, ndev); if (vmbus_proto_version < VERSION_WIN10)
netvsc_teardown_send_gpadl(device, net_device, ndev);
RCU_INIT_POINTER(net_device_ctx->nvdev, NULL);
/* Disable NAPI and disassociate its context from the device. */ for (i = 0; i < net_device->num_chn; i++) { /* See also vmbus_reset_channel_cb(). */ /* only disable enabled NAPI channel */ if (i < ndev->real_num_rx_queues) {
netif_queue_set_napi(ndev, i, NETDEV_QUEUE_TYPE_TX,
NULL);
netif_queue_set_napi(ndev, i, NETDEV_QUEUE_TYPE_RX,
NULL);
napi_disable(&net_device->chan_table[i].napi);
}
/* First check if this is a VMBUS completion without data payload */ if (!msglen) {
cmd_rqst = incoming_channel->request_addr_callback(incoming_channel,
desc->trans_id); if (cmd_rqst == VMBUS_RQST_ERROR) {
netdev_err(ndev, "Invalid transaction ID %llx\n", desc->trans_id); return;
}
/* Ensure packet is big enough to read header fields */ if (msglen < sizeof(struct nvsp_message_header)) {
netdev_err(ndev, "nvsp_message length too small: %u\n", msglen); return;
}
nvsp_packet = hv_pkt_data(desc); switch (nvsp_packet->hdr.msg_type) { case NVSP_MSG_TYPE_INIT_COMPLETE: if (msglen < sizeof(struct nvsp_message_header) + sizeof(struct nvsp_message_init_complete)) {
netdev_err(ndev, "nvsp_msg length too small: %u\n",
msglen); return;
} break;
case NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE: if (msglen < sizeof(struct nvsp_message_header) +
struct_size_t(struct nvsp_1_message_send_receive_buffer_complete,
sections, 1)) {
netdev_err(ndev, "nvsp_msg1 length too small: %u\n",
msglen); return;
} break;
case NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE: if (msglen < sizeof(struct nvsp_message_header) + sizeof(struct nvsp_1_message_send_send_buffer_complete)) {
netdev_err(ndev, "nvsp_msg1 length too small: %u\n",
msglen); return;
} break;
case NVSP_MSG5_TYPE_SUBCHANNEL: if (msglen < sizeof(struct nvsp_message_header) + sizeof(struct nvsp_5_subchannel_complete)) {
netdev_err(ndev, "nvsp_msg5 length too small: %u\n",
msglen); return;
} break;
case NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE: if (msglen < sizeof(struct nvsp_message_header) + sizeof(struct nvsp_1_message_send_rndis_packet_complete)) { if (net_ratelimit())
netdev_err(ndev, "nvsp_rndis_pkt_complete length too small: %u\n",
msglen); return;
}
/* If status indicates an error, output a message so we know *there'saproblem.Butprocessthecompletionanywaysothe *resourcesarereleased.
*/
status = nvsp_packet->msg.v1_msg.send_rndis_pkt_complete.status; if (status != NVSP_STAT_SUCCESS && net_ratelimit())
netdev_err(ndev, "nvsp_rndis_pkt_complete error status: %x\n",
status);
for (i = 0; i < page_count; i++) { char *src = phys_to_virt(pb[i].pfn << HV_HYP_PAGE_SHIFT);
u32 offset = pb[i].offset;
u32 len = pb[i].len;
memcpy(dest, (src + offset), len);
dest += len;
}
if (padding)
memset(dest, 0, padding);
}
void netvsc_dma_unmap(struct hv_device *hv_dev, struct hv_netvsc_packet *packet)
{ int i;
if (!hv_is_isolation_supported()) return;
if (!packet->dma_range) return;
for (i = 0; i < packet->page_buf_cnt; i++)
dma_unmap_single(&hv_dev->device, packet->dma_range[i].dma,
packet->dma_range[i].mapping_size,
DMA_TO_DEVICE);
/* Send a control message or XDP packet directly without accessing *msd(Multi-SendData)fieldwhichmaybechangedduringdatapacket *processing.
*/ if (!skb || xdp_tx) return netvsc_send_pkt(device, packet, net_device, pb, skb);
/* batch packets in send buffer if possible */
msdp = &nvchan->msd; if (msdp->pkt)
msd_len = msdp->pkt->total_data_buflen;
/* Keep aggregating only if stack says more data is coming *andnotdoingmixedmodessendandnotflowblocked
*/
xmit_more = netdev_xmit_more() &&
!packet->cp_partial &&
!netif_xmit_stopped(netdev_get_tx_queue(ndev, packet->q_idx));
/* Ensure packet is big enough to read header fields */ if (msglen < sizeof(struct nvsp_message_header)) {
netif_err(net_device_ctx, rx_err, ndev, "invalid nvsp header, length too small: %u\n",
msglen); return0;
}
/* Make sure this is a valid nvsp packet */ if (unlikely(nvsp->hdr.msg_type != NVSP_MSG1_TYPE_SEND_RNDIS_PKT)) {
netif_err(net_device_ctx, rx_err, ndev, "Unknown nvsp packet type received %u\n",
nvsp->hdr.msg_type); return0;
}
if (unlikely(vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID)) {
netif_err(net_device_ctx, rx_err, ndev, "Invalid xfer page set id - expecting %x got %x\n",
NETVSC_RECEIVE_BUFFER_ID,
vmxferpage_packet->xfer_pageset_id); return0;
}
count = vmxferpage_packet->range_cnt;
/* Check count for a valid value */ if (NETVSC_XFER_HEADER_SIZE(count) > desc->offset8 << 3) {
netif_err(net_device_ctx, rx_err, ndev, "Range count is not valid: %d\n",
count); return0;
}
/* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ for (i = 0; i < count; i++) {
u32 offset = vmxferpage_packet->ranges[i].byte_offset;
u32 buflen = vmxferpage_packet->ranges[i].byte_count; void *data; int ret;
if (unlikely(offset > net_device->recv_buf_size ||
buflen > net_device->recv_buf_size - offset)) {
nvchan->rsc.cnt = 0;
status = NVSP_STAT_FAIL;
netif_err(net_device_ctx, rx_err, ndev, "Packet offset:%u + len:%u too big\n",
offset, buflen);
continue;
}
/* We're going to copy (sections of) the packet into nvchan->recv_buf; *makesurethatnvchan->recv_bufislargeenoughtoholdthepacket.
*/ if (unlikely(buflen > net_device->recv_section_size)) {
nvchan->rsc.cnt = 0;
status = NVSP_STAT_FAIL;
netif_err(net_device_ctx, rx_err, ndev, "Packet too big: buflen=%u recv_section_size=%u\n",
buflen, net_device->recv_section_size);
continue;
}
data = recv_buf + offset;
nvchan->rsc.is_last = (i == count - 1);
trace_rndis_recv(ndev, q_idx, data);
/* Pass it to the upper layer */
ret = rndis_filter_receive(ndev, net_device,
nvchan, data, buflen);
if (unlikely(ret != NVSP_STAT_SUCCESS)) { /* Drop incomplete packet */
nvchan->rsc.cnt = 0;
status = NVSP_STAT_FAIL;
}
}
/* Ensure packet is big enough to read its fields */ if (msglen < sizeof(struct nvsp_message_header) + sizeof(struct nvsp_4_send_vf_association)) {
netdev_err(ndev, "nvsp_v4_msg length too small: %u\n", msglen); return;
}
/* Ensure packet is big enough to read header fields */ if (msglen < sizeof(struct nvsp_message_header)) {
netdev_err(ndev, "inband nvsp_message length too small: %u\n", msglen); return;
}
switch (nvmsg->hdr.msg_type) { case NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE:
netvsc_send_table(ndev, nvscdev, nvmsg, msglen); break;
case NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION: if (hv_is_isolation_supported())
netdev_err(ndev, "Ignore VF_ASSOCIATION msg from the host supporting isolation\n"); else
netvsc_send_vf(ndev, nvmsg, msglen); break;
}
}
/* Send any pending receive completions */
ret = send_recv_completions(ndev, net_device, nvchan);
/* If it did not exhaust NAPI budget this time *andnotdoingbusypoll *thenre-enablehostinterrupts *andrescheduleifringisnotempty *orsendingreceivecompletionfailed.
*/ if (work_done < budget &&
napi_complete_done(napi, work_done) &&
(ret || hv_end_read(&channel->inbound)) &&
napi_schedule_prep(napi)) {
hv_begin_read(&channel->inbound);
__napi_schedule(napi);
}
/* Driver may overshoot since multiple packets per descriptor */ return min(work_done, budget);
}
/* Call back when data is available in host ring buffer. *Processingisdeferreduntilnetworksoftirq(NAPI)
*/ void netvsc_channel_cb(void *context)
{ struct netvsc_channel *nvchan = context; struct vmbus_channel *channel = nvchan->channel; struct hv_ring_buffer_info *rbi = &channel->inbound;
/* preload first vmpacket descriptor */
prefetch(hv_get_ring_buffer(rbi) + rbi->priv_read_index);
if (napi_schedule_prep(&nvchan->napi)) { /* disable interrupts from host */
hv_begin_read(rbi);
net_device = alloc_net_device(); if (!net_device) return ERR_PTR(-ENOMEM);
for (i = 0; i < VRSS_SEND_TAB_SIZE; i++)
net_device_ctx->tx_table[i] = 0;
/* Because the device uses NAPI, all the interrupt batching and *controlisdoneviaNetsoftirq,notthechannelhandling
*/
set_channel_read_mode(device->channel, HV_CALL_ISR);
/* If we're reopening the device we may have multiple queues, fill the *chn_tablewiththedefaultchanneltouseitbeforesubchannelsare *opened. *Initializethechannelstatebeforeweopen; *wecanbeinterruptedassoonasweopenthechannel.
*/
for (i = 0; i < VRSS_CHANNEL_MAX; i++) { struct netvsc_channel *nvchan = &net_device->chan_table[i];
/* Enable NAPI handler before init callbacks */
netif_napi_add(ndev, &net_device->chan_table[0].napi, netvsc_poll);
napi_enable(&net_device->chan_table[0].napi);
netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_RX,
&net_device->chan_table[0].napi);
netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_TX,
&net_device->chan_table[0].napi);
/* Open the channel */
device->channel->next_request_id_callback = vmbus_next_request_id;
device->channel->request_addr_callback = vmbus_request_addr;
device->channel->rqstor_size = netvsc_rqstor_size(netvsc_ring_bytes);
device->channel->max_pkt_size = NETVSC_MAX_PKT_SIZE;
ret = vmbus_open(device->channel, netvsc_ring_bytes,
netvsc_ring_bytes, NULL, 0,
netvsc_channel_cb, net_device->chan_table);
if (ret != 0) {
netdev_err(ndev, "unable to open channel: %d\n", ret); goto cleanup;
}
/* Channel is opened */
netdev_dbg(ndev, "hv_netvsc channel opened successfully\n");
/* Connect with the NetVsp */
ret = netvsc_connect_vsp(device, net_device, device_info); if (ret != 0) {
netdev_err(ndev, "unable to connect to NetVSP - %d\n", ret); goto close;
}
/* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is *populated.
*/
rcu_assign_pointer(net_device_ctx->nvdev, net_device);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.