diff options
Diffstat (limited to 'drivers/net/hyperv')
-rw-r--r-- | drivers/net/hyperv/hyperv_net.h | 50 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 267 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 448 | ||||
-rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 205 |
4 files changed, 617 insertions, 353 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 8b3bd8ecd..591af71ea 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -158,7 +158,7 @@ enum rndis_device_state { }; struct rndis_device { - struct netvsc_device *net_dev; + struct net_device *ndev; enum rndis_device_state state; bool link_state; @@ -173,6 +173,7 @@ struct rndis_device { /* Interface */ struct rndis_message; +struct netvsc_device; int netvsc_device_add(struct hv_device *device, void *additional_info); int netvsc_device_remove(struct hv_device *device); int netvsc_send(struct hv_device *device, @@ -189,8 +190,8 @@ int netvsc_recv_callback(struct hv_device *device_obj, struct vmbus_channel *channel, u16 vlan_tci); void netvsc_channel_cb(void *context); -int rndis_filter_open(struct hv_device *dev); -int rndis_filter_close(struct hv_device *dev); +int rndis_filter_open(struct netvsc_device *nvdev); +int rndis_filter_close(struct netvsc_device *nvdev); int rndis_filter_device_add(struct hv_device *dev, void *additional_info); void rndis_filter_device_remove(struct hv_device *dev); @@ -200,7 +201,9 @@ int rndis_filter_receive(struct hv_device *dev, struct vmbus_channel *channel); int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); -int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); +int rndis_filter_set_device_mac(struct net_device *ndev, char *mac); + +void netvsc_switch_datapath(struct net_device *nv_dev, bool vf); #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) @@ -645,6 +648,8 @@ struct netvsc_reconfig { struct net_device_context { /* point back to our device context */ struct hv_device *device_ctx; + /* netvsc_device */ + struct netvsc_device *nvdev; /* reconfigure work */ struct delayed_work dwork; /* last reconfig time */ @@ -663,17 +668,26 @@ struct net_device_context { /* Ethtool settings */ u8 duplex; u32 speed; + + /* the device is going away */ + bool start_remove; + + /* State to manage the associated VF interface. */ + struct net_device *vf_netdev; + bool vf_inject; + atomic_t vf_use_cnt; + /* 1: allocated, serial number is valid. 0: not allocated */ + u32 vf_alloc; + /* Serial number of the VF to team with */ + u32 vf_serial; }; /* Per netvsc device */ struct netvsc_device { - struct hv_device *dev; - u32 nvsp_version; atomic_t num_outstanding_sends; wait_queue_head_t wait_drain; - bool start_remove; bool destroy; /* Receive buffer allocated by us but manages by NetVSP */ @@ -699,8 +713,6 @@ struct netvsc_device { struct nvsp_message revoke_packet; /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ - struct net_device *ndev; - struct vmbus_channel *chn_table[VRSS_CHANNEL_MAX]; u32 send_table[VRSS_SEND_TAB_SIZE]; u32 max_chn; @@ -723,15 +735,21 @@ struct netvsc_device { u32 max_pkt; /* max number of pkt in one send, e.g. 8 */ u32 pkt_align; /* alignment bytes, e.g. 8 */ - /* The net device context */ - struct net_device_context *nd_ctx; - - /* 1: allocated, serial number is valid. 0: not allocated */ - u32 vf_alloc; - /* Serial number of the VF to team with */ - u32 vf_serial; + atomic_t open_cnt; }; +static inline struct netvsc_device * +net_device_to_netvsc_device(struct net_device *ndev) +{ + return ((struct net_device_context *)netdev_priv(ndev))->nvdev; +} + +static inline struct netvsc_device * +hv_device_to_netvsc_device(struct hv_device *device) +{ + return net_device_to_netvsc_device(hv_get_drvdata(device)); +} + /* NdisInitialize message */ struct rndis_initialize_request { u32 req_id; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index ec313fc08..410fb8e81 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -33,11 +33,36 @@ #include "hyperv_net.h" +/* + * Switch the data path from the synthetic interface to the VF + * interface. + */ +void netvsc_switch_datapath(struct net_device *ndev, bool vf) +{ + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct hv_device *dev = net_device_ctx->device_ctx; + struct netvsc_device *nv_dev = net_device_ctx->nvdev; + struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt; + + memset(init_pkt, 0, sizeof(struct nvsp_message)); + init_pkt->hdr.msg_type = NVSP_MSG4_TYPE_SWITCH_DATA_PATH; + if (vf) + init_pkt->msg.v4_msg.active_dp.active_datapath = + NVSP_DATAPATH_VF; + else + init_pkt->msg.v4_msg.active_dp.active_datapath = + NVSP_DATAPATH_SYNTHETIC; -static struct netvsc_device *alloc_net_device(struct hv_device *device) + vmbus_sendpacket(dev->channel, init_pkt, + sizeof(struct nvsp_message), + (unsigned long)init_pkt, + VM_PKT_DATA_INBAND, 0); +} + + +static struct netvsc_device *alloc_net_device(void) { struct netvsc_device *net_device; - struct net_device *ndev = hv_get_drvdata(device); net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); if (!net_device) @@ -50,14 +75,11 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) } init_waitqueue_head(&net_device->wait_drain); - net_device->start_remove = false; net_device->destroy = false; - net_device->dev = device; - net_device->ndev = ndev; + atomic_set(&net_device->open_cnt, 0); net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT; net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT; - hv_set_drvdata(device, net_device); return net_device; } @@ -69,9 +91,8 @@ static void free_netvsc_device(struct netvsc_device *nvdev) static struct netvsc_device *get_outbound_net_device(struct hv_device *device) { - struct netvsc_device *net_device; + struct netvsc_device *net_device = hv_device_to_netvsc_device(device); - net_device = hv_get_drvdata(device); if (net_device && net_device->destroy) net_device = NULL; @@ -80,9 +101,7 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) static struct netvsc_device *get_inbound_net_device(struct hv_device *device) { - struct netvsc_device *net_device; - - net_device = hv_get_drvdata(device); + struct netvsc_device *net_device = hv_device_to_netvsc_device(device); if (!net_device) goto get_in_err; @@ -96,11 +115,12 @@ get_in_err: } -static int netvsc_destroy_buf(struct netvsc_device *net_device) +static int netvsc_destroy_buf(struct hv_device *device) { struct nvsp_message *revoke_packet; int ret = 0; - struct net_device *ndev = net_device->ndev; + struct net_device *ndev = hv_get_drvdata(device); + struct netvsc_device *net_device = net_device_to_netvsc_device(ndev); /* * If we got a section count, it means we received a @@ -118,7 +138,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) revoke_packet->msg.v1_msg. revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; - ret = vmbus_sendpacket(net_device->dev->channel, + ret = vmbus_sendpacket(device->channel, revoke_packet, sizeof(struct nvsp_message), (unsigned long)revoke_packet, @@ -136,8 +156,8 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) /* Teardown the gpadl on the vsp end */ if (net_device->recv_buf_gpadl_handle) { - ret = vmbus_teardown_gpadl(net_device->dev->channel, - net_device->recv_buf_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 * rather than continue and a bugchk @@ -178,7 +198,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) revoke_packet->msg.v1_msg.revoke_send_buf.id = NETVSC_SEND_BUFFER_ID; - ret = vmbus_sendpacket(net_device->dev->channel, + ret = vmbus_sendpacket(device->channel, revoke_packet, sizeof(struct nvsp_message), (unsigned long)revoke_packet, @@ -194,7 +214,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) } /* Teardown the gpadl on the vsp end */ if (net_device->send_buf_gpadl_handle) { - ret = vmbus_teardown_gpadl(net_device->dev->channel, + 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 @@ -220,7 +240,6 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) static int netvsc_init_buf(struct hv_device *device) { int ret = 0; - unsigned long t; struct netvsc_device *net_device; struct nvsp_message *init_packet; struct net_device *ndev; @@ -229,7 +248,7 @@ static int netvsc_init_buf(struct hv_device *device) net_device = get_outbound_net_device(device); if (!net_device) return -ENODEV; - ndev = net_device->ndev; + ndev = hv_get_drvdata(device); node = cpu_to_node(device->channel->target_cpu); net_device->recv_buf = vzalloc_node(net_device->recv_buf_size, node); @@ -281,9 +300,7 @@ static int netvsc_init_buf(struct hv_device *device) goto cleanup; } - t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); - BUG_ON(t == 0); - + wait_for_completion(&net_device->channel_init_wait); /* Check the response */ if (init_packet->msg.v1_msg. @@ -366,8 +383,7 @@ static int netvsc_init_buf(struct hv_device *device) goto cleanup; } - t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); - BUG_ON(t == 0); + wait_for_completion(&net_device->channel_init_wait); /* Check the response */ if (init_packet->msg.v1_msg. @@ -406,7 +422,7 @@ static int netvsc_init_buf(struct hv_device *device) goto exit; cleanup: - netvsc_destroy_buf(net_device); + netvsc_destroy_buf(device); exit: return ret; @@ -419,8 +435,8 @@ static int negotiate_nvsp_ver(struct hv_device *device, struct nvsp_message *init_packet, u32 nvsp_ver) { + struct net_device *ndev = hv_get_drvdata(device); int ret; - unsigned long t; memset(init_packet, 0, sizeof(struct nvsp_message)); init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT; @@ -437,10 +453,7 @@ static int negotiate_nvsp_ver(struct hv_device *device, if (ret != 0) return ret; - t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); - - if (t == 0) - return -ETIMEDOUT; + wait_for_completion(&net_device->channel_init_wait); if (init_packet->msg.init_msg.init_complete.status != NVSP_STAT_SUCCESS) @@ -452,8 +465,7 @@ static int negotiate_nvsp_ver(struct hv_device *device, /* NVSPv2 or later: Send NDIS config */ memset(init_packet, 0, sizeof(struct nvsp_message)); init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; - init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu + - ETH_HLEN; + init_packet->msg.v2_msg.send_ndis_config.mtu = ndev->mtu + ETH_HLEN; init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1; if (nvsp_ver >= NVSP_PROTOCOL_VERSION_5) @@ -473,7 +485,6 @@ static int netvsc_connect_vsp(struct hv_device *device) struct netvsc_device *net_device; struct nvsp_message *init_packet; int ndis_version; - struct net_device *ndev; u32 ver_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2, NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5 }; int i, num_ver = 4; /* number of different NVSP versions */ @@ -481,7 +492,6 @@ static int netvsc_connect_vsp(struct hv_device *device) net_device = get_outbound_net_device(device); if (!net_device) return -ENODEV; - ndev = net_device->ndev; init_packet = &net_device->channel_init_pkt; @@ -537,9 +547,9 @@ cleanup: return ret; } -static void netvsc_disconnect_vsp(struct netvsc_device *net_device) +static void netvsc_disconnect_vsp(struct hv_device *device) { - netvsc_destroy_buf(net_device); + netvsc_destroy_buf(device); } /* @@ -547,24 +557,13 @@ static void netvsc_disconnect_vsp(struct netvsc_device *net_device) */ int netvsc_device_remove(struct hv_device *device) { - struct netvsc_device *net_device; - unsigned long flags; - - net_device = hv_get_drvdata(device); - - netvsc_disconnect_vsp(net_device); + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_device = net_device_ctx->nvdev; - /* - * Since we have already drained, we don't need to busy wait - * as was done in final_release_stor_device() - * Note that we cannot set the ext pointer to NULL until - * we have drained - to drain the outgoing packets, we need to - * allow incoming packets. - */ + netvsc_disconnect_vsp(device); - spin_lock_irqsave(&device->channel->inbound_lock, flags); - hv_set_drvdata(device, NULL); - spin_unlock_irqrestore(&device->channel->inbound_lock, flags); + net_device_ctx->nvdev = NULL; /* * At this point, no one should be accessing net_device @@ -612,12 +611,11 @@ static void netvsc_send_completion(struct netvsc_device *net_device, { struct nvsp_message *nvsp_packet; struct hv_netvsc_packet *nvsc_packet; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); u32 send_index; struct sk_buff *skb; - ndev = net_device->ndev; - nvsp_packet = (struct nvsp_message *)((unsigned long)packet + (packet->offset8 << 3)); @@ -662,7 +660,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device, wake_up(&net_device->wait_drain); if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) && - !net_device->start_remove && + !net_device_ctx->start_remove && (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) netif_tx_wake_queue(netdev_get_tx_queue( @@ -746,6 +744,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, } static inline int netvsc_send_pkt( + struct hv_device *device, struct hv_netvsc_packet *packet, struct netvsc_device *net_device, struct hv_page_buffer **pb, @@ -754,7 +753,7 @@ static inline int netvsc_send_pkt( struct nvsp_message nvmsg; u16 q_idx = packet->q_idx; struct vmbus_channel *out_channel = net_device->chn_table[q_idx]; - struct net_device *ndev = net_device->ndev; + struct net_device *ndev = hv_get_drvdata(device); u64 req_id; int ret; struct hv_page_buffer *pgbuf; @@ -949,7 +948,8 @@ int netvsc_send(struct hv_device *device, } if (msd_send) { - m_ret = netvsc_send_pkt(msd_send, net_device, NULL, msd_skb); + m_ret = netvsc_send_pkt(device, msd_send, net_device, + NULL, msd_skb); if (m_ret != 0) { netvsc_free_send_slot(net_device, @@ -960,7 +960,7 @@ int netvsc_send(struct hv_device *device, send_now: if (cur_send) - ret = netvsc_send_pkt(cur_send, net_device, pb, skb); + ret = netvsc_send_pkt(device, cur_send, net_device, pb, skb); if (ret != 0 && section_index != NETVSC_INVALID_INDEX) netvsc_free_send_slot(net_device, section_index); @@ -976,9 +976,7 @@ static void netvsc_send_recv_completion(struct hv_device *device, struct nvsp_message recvcompMessage; int retries = 0; int ret; - struct net_device *ndev; - - ndev = net_device->ndev; + struct net_device *ndev = hv_get_drvdata(device); recvcompMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; @@ -1025,11 +1023,9 @@ static void netvsc_receive(struct netvsc_device *net_device, u32 status = NVSP_STAT_SUCCESS; int i; int count = 0; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(device); void *data; - ndev = net_device->ndev; - /* * All inbound packets other than send completion should be xfer page * packet @@ -1085,14 +1081,13 @@ static void netvsc_send_table(struct hv_device *hdev, struct nvsp_message *nvmsg) { struct netvsc_device *nvscdev; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(hdev); int i; u32 count, *tab; nvscdev = get_outbound_net_device(hdev); if (!nvscdev) return; - ndev = nvscdev->ndev; count = nvmsg->msg.v5_msg.send_table.count; if (count != VRSS_SEND_TAB_SIZE) { @@ -1107,16 +1102,16 @@ static void netvsc_send_table(struct hv_device *hdev, nvscdev->send_table[i] = tab[i]; } -static void netvsc_send_vf(struct netvsc_device *nvdev, +static void netvsc_send_vf(struct net_device_context *net_device_ctx, struct nvsp_message *nvmsg) { - nvdev->vf_alloc = nvmsg->msg.v4_msg.vf_assoc.allocated; - nvdev->vf_serial = nvmsg->msg.v4_msg.vf_assoc.serial; + net_device_ctx->vf_alloc = nvmsg->msg.v4_msg.vf_assoc.allocated; + net_device_ctx->vf_serial = nvmsg->msg.v4_msg.vf_assoc.serial; } static inline void netvsc_receive_inband(struct hv_device *hdev, - struct netvsc_device *nvdev, - struct nvsp_message *nvmsg) + struct net_device_context *net_device_ctx, + struct nvsp_message *nvmsg) { switch (nvmsg->hdr.msg_type) { case NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE: @@ -1124,11 +1119,45 @@ static inline void netvsc_receive_inband(struct hv_device *hdev, break; case NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION: - netvsc_send_vf(nvdev, nvmsg); + netvsc_send_vf(net_device_ctx, nvmsg); break; } } +static void netvsc_process_raw_pkt(struct hv_device *device, + struct vmbus_channel *channel, + struct netvsc_device *net_device, + struct net_device *ndev, + u64 request_id, + struct vmpacket_descriptor *desc) +{ + struct nvsp_message *nvmsg; + struct net_device_context *net_device_ctx = netdev_priv(ndev); + + nvmsg = (struct nvsp_message *)((unsigned long) + desc + (desc->offset8 << 3)); + + switch (desc->type) { + case VM_PKT_COMP: + netvsc_send_completion(net_device, channel, device, desc); + break; + + case VM_PKT_DATA_USING_XFER_PAGES: + netvsc_receive(net_device, channel, device, desc); + break; + + case VM_PKT_DATA_INBAND: + netvsc_receive_inband(device, net_device_ctx, nvmsg); + break; + + default: + netdev_err(ndev, "unhandled packet type %d, tid %llx\n", + desc->type, request_id); + break; + } +} + + void netvsc_channel_cb(void *context) { int ret; @@ -1141,7 +1170,7 @@ void netvsc_channel_cb(void *context) unsigned char *buffer; int bufferlen = NETVSC_PACKET_SIZE; struct net_device *ndev; - struct nvsp_message *nvmsg; + bool need_to_commit = false; if (channel->primary_channel != NULL) device = channel->primary_channel->device_obj; @@ -1151,43 +1180,40 @@ void netvsc_channel_cb(void *context) net_device = get_inbound_net_device(device); if (!net_device) return; - ndev = net_device->ndev; + ndev = hv_get_drvdata(device); buffer = get_per_channel_state(channel); do { + desc = get_next_pkt_raw(channel); + if (desc != NULL) { + netvsc_process_raw_pkt(device, + channel, + net_device, + ndev, + desc->trans_id, + desc); + + put_pkt_raw(channel, desc); + need_to_commit = true; + continue; + } + if (need_to_commit) { + need_to_commit = false; + commit_rd_index(channel); + } + ret = vmbus_recvpacket_raw(channel, buffer, bufferlen, &bytes_recvd, &request_id); if (ret == 0) { if (bytes_recvd > 0) { desc = (struct vmpacket_descriptor *)buffer; - nvmsg = (struct nvsp_message *)((unsigned long) - desc + (desc->offset8 << 3)); - switch (desc->type) { - case VM_PKT_COMP: - netvsc_send_completion(net_device, - channel, - device, desc); - break; - - case VM_PKT_DATA_USING_XFER_PAGES: - netvsc_receive(net_device, channel, - device, desc); - break; - - case VM_PKT_DATA_INBAND: - netvsc_receive_inband(device, - net_device, - nvmsg); - break; - - default: - netdev_err(ndev, - "unhandled packet type %d, " - "tid %llx len %d\n", - desc->type, request_id, - bytes_recvd); - break; - } + netvsc_process_raw_pkt(device, + channel, + net_device, + ndev, + request_id, + desc); + } else { /* @@ -1224,30 +1250,19 @@ void netvsc_channel_cb(void *context) */ int netvsc_device_add(struct hv_device *device, void *additional_info) { - int ret = 0; + int i, ret = 0; int ring_size = ((struct netvsc_device_info *)additional_info)->ring_size; struct netvsc_device *net_device; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); - net_device = alloc_net_device(device); + net_device = alloc_net_device(); if (!net_device) return -ENOMEM; net_device->ring_size = ring_size; - /* - * Coming into this function, struct net_device * is - * registered as the driver private data. - * In alloc_net_device(), we register struct netvsc_device * - * as the driver private data and stash away struct net_device * - * in struct netvsc_device *. - */ - ndev = net_device->ndev; - - /* Add netvsc_device context to netvsc_device */ - net_device->nd_ctx = netdev_priv(ndev); - /* Initialize the NetVSC channel extension */ init_completion(&net_device->channel_init_wait); @@ -1266,7 +1281,19 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) /* Channel is opened */ pr_info("hv_netvsc channel opened successfully\n"); - net_device->chn_table[0] = device->channel; + /* If we're reopening the device we may have multiple queues, fill the + * chn_table with the default channel to use it before subchannels are + * opened. + */ + for (i = 0; i < VRSS_CHANNEL_MAX; i++) + net_device->chn_table[i] = device->channel; + + /* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is + * populated. + */ + wmb(); + + net_device_ctx->nvdev = net_device; /* Connect with the NetVsp */ ret = netvsc_connect_vsp(device); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index b8121eba3..c4d9653ca 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -67,18 +67,19 @@ static void do_set_multicast(struct work_struct *w) { struct net_device_context *ndevctx = container_of(w, struct net_device_context, work); - struct netvsc_device *nvdev; + struct hv_device *device_obj = ndevctx->device_ctx; + struct net_device *ndev = hv_get_drvdata(device_obj); + struct netvsc_device *nvdev = ndevctx->nvdev; struct rndis_device *rdev; - nvdev = hv_get_drvdata(ndevctx->device_ctx); - if (nvdev == NULL || nvdev->ndev == NULL) + if (!nvdev) return; rdev = nvdev->extension; if (rdev == NULL) return; - if (nvdev->ndev->flags & IFF_PROMISC) + if (ndev->flags & IFF_PROMISC) rndis_filter_set_packet_filter(rdev, NDIS_PACKET_TYPE_PROMISCUOUS); else @@ -97,16 +98,14 @@ static void netvsc_set_multicast_list(struct net_device *net) static int netvsc_open(struct net_device *net) { - struct net_device_context *net_device_ctx = netdev_priv(net); - struct hv_device *device_obj = net_device_ctx->device_ctx; - struct netvsc_device *nvdev; + struct netvsc_device *nvdev = net_device_to_netvsc_device(net); struct rndis_device *rdev; int ret = 0; netif_carrier_off(net); /* Open up the device */ - ret = rndis_filter_open(device_obj); + ret = rndis_filter_open(nvdev); if (ret != 0) { netdev_err(net, "unable to open device (ret %d).\n", ret); return ret; @@ -114,7 +113,6 @@ static int netvsc_open(struct net_device *net) netif_tx_wake_all_queues(net); - nvdev = hv_get_drvdata(device_obj); rdev = nvdev->extension; if (!rdev->link_state) netif_carrier_on(net); @@ -125,8 +123,7 @@ static int netvsc_open(struct net_device *net) static int netvsc_close(struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); - struct hv_device *device_obj = net_device_ctx->device_ctx; - struct netvsc_device *nvdev = hv_get_drvdata(device_obj); + struct netvsc_device *nvdev = net_device_ctx->nvdev; int ret; u32 aread, awrite, i, msec = 10, retry = 0, retry_max = 20; struct vmbus_channel *chn; @@ -135,7 +132,7 @@ static int netvsc_close(struct net_device *net) /* Make sure netvsc_set_multicast_list doesn't re-enable filter! */ cancel_work_sync(&net_device_ctx->work); - ret = rndis_filter_close(device_obj); + ret = rndis_filter_close(nvdev); if (ret != 0) { netdev_err(net, "unable to close device (ret %d).\n", ret); return ret; @@ -205,8 +202,7 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback) { struct net_device_context *net_device_ctx = netdev_priv(ndev); - struct hv_device *hdev = net_device_ctx->device_ctx; - struct netvsc_device *nvsc_dev = hv_get_drvdata(hdev); + struct netvsc_device *nvsc_dev = net_device_ctx->nvdev; u32 hash; u16 q_idx = 0; @@ -580,7 +576,6 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, struct rndis_indicate_status *indicate = &resp->msg.indicate_status; struct net_device *net; struct net_device_context *ndev_ctx; - struct netvsc_device *net_device; struct netvsc_reconfig *event; unsigned long flags; @@ -590,8 +585,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT) return; - net_device = hv_get_drvdata(device_obj); - net = net_device->ndev; + net = hv_get_drvdata(device_obj); if (!net || net->reg_state != NETREG_REGISTERED) return; @@ -610,6 +604,47 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, schedule_delayed_work(&ndev_ctx->dwork, 0); } + +static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net, + struct hv_netvsc_packet *packet, + struct ndis_tcp_ip_checksum_info *csum_info, + void *data, u16 vlan_tci) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen); + if (!skb) + return skb; + + /* + * Copy to skb. This copy is needed here since the memory pointed by + * hv_netvsc_packet cannot be deallocated + */ + memcpy(skb_put(skb, packet->total_data_buflen), data, + packet->total_data_buflen); + + skb->protocol = eth_type_trans(skb, net); + + /* skb is already created with CHECKSUM_NONE */ + skb_checksum_none_assert(skb); + + /* + * In Linux, the IP checksum is always checked. + * Do L4 checksum offload if enabled and present. + */ + if (csum_info && (net->features & NETIF_F_RXCSUM)) { + if (csum_info->receive.tcp_checksum_succeeded || + csum_info->receive.udp_checksum_succeeded) + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + + if (vlan_tci & VLAN_TAG_PRESENT) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + vlan_tci); + + return skb; +} + /* * netvsc_recv_callback - Callback when we receive a packet from the * "wire" on the specified device. @@ -621,48 +656,60 @@ int netvsc_recv_callback(struct hv_device *device_obj, struct vmbus_channel *channel, u16 vlan_tci) { - struct net_device *net; - struct net_device_context *net_device_ctx; + struct net_device *net = hv_get_drvdata(device_obj); + struct net_device_context *net_device_ctx = netdev_priv(net); struct sk_buff *skb; + struct sk_buff *vf_skb; struct netvsc_stats *rx_stats; + u32 bytes_recvd = packet->total_data_buflen; + int ret = 0; - net = ((struct netvsc_device *)hv_get_drvdata(device_obj))->ndev; - if (!net || net->reg_state != NETREG_REGISTERED) { + if (!net || net->reg_state != NETREG_REGISTERED) return NVSP_STAT_FAIL; + + if (READ_ONCE(net_device_ctx->vf_inject)) { + atomic_inc(&net_device_ctx->vf_use_cnt); + if (!READ_ONCE(net_device_ctx->vf_inject)) { + /* + * We raced; just move on. + */ + atomic_dec(&net_device_ctx->vf_use_cnt); + goto vf_injection_done; + } + + /* + * Inject this packet into the VF inerface. + * On Hyper-V, multicast and brodcast packets + * are only delivered on the synthetic interface + * (after subjecting these to policy filters on + * the host). Deliver these via the VF interface + * in the guest. + */ + vf_skb = netvsc_alloc_recv_skb(net_device_ctx->vf_netdev, + packet, csum_info, *data, + vlan_tci); + if (vf_skb != NULL) { + ++net_device_ctx->vf_netdev->stats.rx_packets; + net_device_ctx->vf_netdev->stats.rx_bytes += + bytes_recvd; + netif_receive_skb(vf_skb); + } else { + ++net->stats.rx_dropped; + ret = NVSP_STAT_FAIL; + } + atomic_dec(&net_device_ctx->vf_use_cnt); + return ret; } - net_device_ctx = netdev_priv(net); + +vf_injection_done: rx_stats = this_cpu_ptr(net_device_ctx->rx_stats); /* Allocate a skb - TODO direct I/O to pages? */ - skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen); + skb = netvsc_alloc_recv_skb(net, packet, csum_info, *data, vlan_tci); if (unlikely(!skb)) { ++net->stats.rx_dropped; return NVSP_STAT_FAIL; } - - /* - * Copy to skb. This copy is needed here since the memory pointed by - * hv_netvsc_packet cannot be deallocated - */ - memcpy(skb_put(skb, packet->total_data_buflen), *data, - packet->total_data_buflen); - - skb->protocol = eth_type_trans(skb, net); - if (csum_info) { - /* We only look at the IP checksum here. - * Should we be dropping the packet if checksum - * failed? How do we deal with other checksums - TCP/UDP? - */ - if (csum_info->receive.ip_checksum_succeeded) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - } - - if (vlan_tci & VLAN_TAG_PRESENT) - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), - vlan_tci); - skb_record_rx_queue(skb, channel-> offermsg.offer.sub_channel_index); @@ -692,8 +739,7 @@ static void netvsc_get_channels(struct net_device *net, struct ethtool_channels *channel) { struct net_device_context *net_device_ctx = netdev_priv(net); - struct hv_device *dev = net_device_ctx->device_ctx; - struct netvsc_device *nvdev = hv_get_drvdata(dev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; if (nvdev) { channel->max_combined = nvdev->max_chn; @@ -706,14 +752,14 @@ static int netvsc_set_channels(struct net_device *net, { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *dev = net_device_ctx->device_ctx; - struct netvsc_device *nvdev = hv_get_drvdata(dev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; struct netvsc_device_info device_info; u32 num_chn; u32 max_chn; int ret = 0; bool recovering = false; - if (!nvdev || nvdev->destroy) + if (net_device_ctx->start_remove || !nvdev || nvdev->destroy) return -ENODEV; num_chn = nvdev->num_chn; @@ -742,14 +788,11 @@ static int netvsc_set_channels(struct net_device *net, goto out; do_set: - nvdev->start_remove = true; + net_device_ctx->start_remove = true; rndis_filter_device_remove(dev); nvdev->num_chn = channels->combined_count; - net_device_ctx->device_ctx = dev; - hv_set_drvdata(dev, net); - memset(&device_info, 0, sizeof(device_info)); device_info.num_chn = nvdev->num_chn; /* passed to RNDIS */ device_info.ring_size = ring_size; @@ -764,7 +807,7 @@ static int netvsc_set_channels(struct net_device *net, goto recover; } - nvdev = hv_get_drvdata(dev); + nvdev = net_device_ctx->nvdev; ret = netif_set_real_num_tx_queues(net, nvdev->num_chn); if (ret) { @@ -786,6 +829,9 @@ static int netvsc_set_channels(struct net_device *net, out: netvsc_open(net); + net_device_ctx->start_remove = false; + /* We may have missed link change notifications */ + schedule_delayed_work(&net_device_ctx->dwork, 0); return ret; @@ -854,14 +900,14 @@ static int netvsc_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); - struct hv_device *hdev = ndevctx->device_ctx; - struct netvsc_device *nvdev = hv_get_drvdata(hdev); + struct netvsc_device *nvdev = ndevctx->nvdev; + struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; int limit = ETH_DATA_LEN; u32 num_chn; int ret = 0; - if (nvdev == NULL || nvdev->destroy) + if (ndevctx->start_remove || !nvdev || nvdev->destroy) return -ENODEV; if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) @@ -876,14 +922,11 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) num_chn = nvdev->num_chn; - nvdev->start_remove = true; + ndevctx->start_remove = true; rndis_filter_device_remove(hdev); ndev->mtu = mtu; - ndevctx->device_ctx = hdev; - hv_set_drvdata(hdev, ndev); - memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = num_chn; @@ -892,6 +935,10 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) out: netvsc_open(ndev); + ndevctx->start_remove = false; + + /* We may have missed link change notifications */ + schedule_delayed_work(&ndevctx->dwork, 0); return ret; } @@ -939,8 +986,6 @@ static struct rtnl_link_stats64 *netvsc_get_stats64(struct net_device *net, static int netvsc_set_mac_addr(struct net_device *ndev, void *p) { - struct net_device_context *ndevctx = netdev_priv(ndev); - struct hv_device *hdev = ndevctx->device_ctx; struct sockaddr *addr = p; char save_adr[ETH_ALEN]; unsigned char save_aatype; @@ -953,7 +998,7 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p) if (err != 0) return err; - err = rndis_filter_set_device_mac(hdev, addr->sa_data); + err = rndis_filter_set_device_mac(ndev, addr->sa_data); if (err != 0) { /* roll back to saved MAC */ memcpy(ndev->dev_addr, save_adr, ETH_ALEN); @@ -1004,18 +1049,22 @@ static const struct net_device_ops device_ops = { */ static void netvsc_link_change(struct work_struct *w) { - struct net_device_context *ndev_ctx; - struct net_device *net; + struct net_device_context *ndev_ctx = + container_of(w, struct net_device_context, dwork.work); + struct hv_device *device_obj = ndev_ctx->device_ctx; + struct net_device *net = hv_get_drvdata(device_obj); struct netvsc_device *net_device; struct rndis_device *rdev; struct netvsc_reconfig *event = NULL; bool notify = false, reschedule = false; unsigned long flags, next_reconfig, delay; - ndev_ctx = container_of(w, struct net_device_context, dwork.work); - net_device = hv_get_drvdata(ndev_ctx->device_ctx); + rtnl_lock(); + if (ndev_ctx->start_remove) + goto out_unlock; + + net_device = ndev_ctx->nvdev; rdev = net_device->extension; - net = net_device->ndev; next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT; if (time_is_after_jiffies(next_reconfig)) { @@ -1026,7 +1075,7 @@ static void netvsc_link_change(struct work_struct *w) delay = next_reconfig - jiffies; delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT; schedule_delayed_work(&ndev_ctx->dwork, delay); - return; + goto out_unlock; } ndev_ctx->last_reconfig = jiffies; @@ -1040,9 +1089,7 @@ static void netvsc_link_change(struct work_struct *w) spin_unlock_irqrestore(&ndev_ctx->lock, flags); if (!event) - return; - - rtnl_lock(); + goto out_unlock; switch (event->event) { /* Only the following events are possible due to the check in @@ -1074,7 +1121,7 @@ static void netvsc_link_change(struct work_struct *w) netif_tx_stop_all_queues(net); event->event = RNDIS_STATUS_MEDIA_CONNECT; spin_lock_irqsave(&ndev_ctx->lock, flags); - list_add_tail(&event->list, &ndev_ctx->reconfig_events); + list_add(&event->list, &ndev_ctx->reconfig_events); spin_unlock_irqrestore(&ndev_ctx->lock, flags); reschedule = true; } @@ -1091,6 +1138,11 @@ static void netvsc_link_change(struct work_struct *w) */ if (reschedule) schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT); + + return; + +out_unlock: + rtnl_unlock(); } static void netvsc_free_netdev(struct net_device *netdev) @@ -1102,6 +1154,176 @@ static void netvsc_free_netdev(struct net_device *netdev) free_netdev(netdev); } +static struct net_device *get_netvsc_net_device(char *mac) +{ + struct net_device *dev, *found = NULL; + int rtnl_locked; + + rtnl_locked = rtnl_trylock(); + + for_each_netdev(&init_net, dev) { + if (memcmp(dev->dev_addr, mac, ETH_ALEN) == 0) { + if (dev->netdev_ops != &device_ops) + continue; + found = dev; + break; + } + } + if (rtnl_locked) + rtnl_unlock(); + + return found; +} + +static int netvsc_register_vf(struct net_device *vf_netdev) +{ + struct net_device *ndev; + struct net_device_context *net_device_ctx; + struct netvsc_device *netvsc_dev; + const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops; + + if (eth_ops == NULL || eth_ops == ðtool_ops) + return NOTIFY_DONE; + + /* + * We will use the MAC address to locate the synthetic interface to + * associate with the VF interface. If we don't find a matching + * synthetic interface, move on. + */ + ndev = get_netvsc_net_device(vf_netdev->dev_addr); + if (!ndev) + return NOTIFY_DONE; + + net_device_ctx = netdev_priv(ndev); + netvsc_dev = net_device_ctx->nvdev; + if (!netvsc_dev || net_device_ctx->vf_netdev) + return NOTIFY_DONE; + + netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); + /* + * Take a reference on the module. + */ + try_module_get(THIS_MODULE); + net_device_ctx->vf_netdev = vf_netdev; + return NOTIFY_OK; +} + +static void netvsc_inject_enable(struct net_device_context *net_device_ctx) +{ + net_device_ctx->vf_inject = true; +} + +static void netvsc_inject_disable(struct net_device_context *net_device_ctx) +{ + net_device_ctx->vf_inject = false; + + /* Wait for currently active users to drain out. */ + while (atomic_read(&net_device_ctx->vf_use_cnt) != 0) + udelay(50); +} + +static int netvsc_vf_up(struct net_device *vf_netdev) +{ + struct net_device *ndev; + struct netvsc_device *netvsc_dev; + const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops; + struct net_device_context *net_device_ctx; + + if (eth_ops == ðtool_ops) + return NOTIFY_DONE; + + ndev = get_netvsc_net_device(vf_netdev->dev_addr); + if (!ndev) + return NOTIFY_DONE; + + net_device_ctx = netdev_priv(ndev); + netvsc_dev = net_device_ctx->nvdev; + + if (!netvsc_dev || !net_device_ctx->vf_netdev) + return NOTIFY_DONE; + + netdev_info(ndev, "VF up: %s\n", vf_netdev->name); + netvsc_inject_enable(net_device_ctx); + + /* + * Open the device before switching data path. + */ + rndis_filter_open(netvsc_dev); + + /* + * notify the host to switch the data path. + */ + netvsc_switch_datapath(ndev, true); + netdev_info(ndev, "Data path switched to VF: %s\n", vf_netdev->name); + + netif_carrier_off(ndev); + + /* Now notify peers through VF device. */ + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, vf_netdev); + + return NOTIFY_OK; +} + + +static int netvsc_vf_down(struct net_device *vf_netdev) +{ + struct net_device *ndev; + struct netvsc_device *netvsc_dev; + struct net_device_context *net_device_ctx; + const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops; + + if (eth_ops == ðtool_ops) + return NOTIFY_DONE; + + ndev = get_netvsc_net_device(vf_netdev->dev_addr); + if (!ndev) + return NOTIFY_DONE; + + net_device_ctx = netdev_priv(ndev); + netvsc_dev = net_device_ctx->nvdev; + + if (!netvsc_dev || !net_device_ctx->vf_netdev) + return NOTIFY_DONE; + + netdev_info(ndev, "VF down: %s\n", vf_netdev->name); + netvsc_inject_disable(net_device_ctx); + netvsc_switch_datapath(ndev, false); + netdev_info(ndev, "Data path switched from VF: %s\n", vf_netdev->name); + rndis_filter_close(netvsc_dev); + netif_carrier_on(ndev); + + /* Now notify peers through netvsc device. */ + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, ndev); + + return NOTIFY_OK; +} + + +static int netvsc_unregister_vf(struct net_device *vf_netdev) +{ + struct net_device *ndev; + struct netvsc_device *netvsc_dev; + const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops; + struct net_device_context *net_device_ctx; + + if (eth_ops == ðtool_ops) + return NOTIFY_DONE; + + ndev = get_netvsc_net_device(vf_netdev->dev_addr); + if (!ndev) + return NOTIFY_DONE; + + net_device_ctx = netdev_priv(ndev); + netvsc_dev = net_device_ctx->nvdev; + if (!netvsc_dev || !net_device_ctx->vf_netdev) + return NOTIFY_DONE; + netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name); + netvsc_inject_disable(net_device_ctx); + net_device_ctx->vf_netdev = NULL; + module_put(THIS_MODULE); + return NOTIFY_OK; +} + static int netvsc_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) { @@ -1138,12 +1360,19 @@ static int netvsc_probe(struct hv_device *dev, } hv_set_drvdata(dev, net); + + net_device_ctx->start_remove = false; + INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change); INIT_WORK(&net_device_ctx->work, do_set_multicast); spin_lock_init(&net_device_ctx->lock); INIT_LIST_HEAD(&net_device_ctx->reconfig_events); + atomic_set(&net_device_ctx->vf_use_cnt, 0); + net_device_ctx->vf_netdev = NULL; + net_device_ctx->vf_inject = false; + net->netdev_ops = &device_ops; net->hw_features = NETVSC_HW_FEATURES; @@ -1168,7 +1397,7 @@ static int netvsc_probe(struct hv_device *dev, } memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); - nvdev = hv_get_drvdata(dev); + nvdev = net_device_ctx->nvdev; netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); @@ -1190,17 +1419,24 @@ static int netvsc_remove(struct hv_device *dev) struct net_device_context *ndev_ctx; struct netvsc_device *net_device; - net_device = hv_get_drvdata(dev); - net = net_device->ndev; + net = hv_get_drvdata(dev); if (net == NULL) { dev_err(&dev->device, "No net device to remove\n"); return 0; } - net_device->start_remove = true; ndev_ctx = netdev_priv(net); + net_device = ndev_ctx->nvdev; + + /* Avoid racing with netvsc_change_mtu()/netvsc_set_channels() + * removing the device. + */ + rtnl_lock(); + ndev_ctx->start_remove = true; + rtnl_unlock(); + cancel_delayed_work_sync(&ndev_ctx->dwork); cancel_work_sync(&ndev_ctx->work); @@ -1215,6 +1451,8 @@ static int netvsc_remove(struct hv_device *dev) */ rndis_filter_device_remove(dev); + hv_set_drvdata(dev, NULL); + netvsc_free_netdev(net); return 0; } @@ -1235,19 +1473,67 @@ static struct hv_driver netvsc_drv = { .remove = netvsc_remove, }; + +/* + * On Hyper-V, every VF interface is matched with a corresponding + * synthetic interface. The synthetic interface is presented first + * to the guest. When the corresponding VF instance is registered, + * we will take care of switching the data path. + */ +static int netvsc_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); + + /* Avoid Vlan dev with same MAC registering as VF */ + if (event_dev->priv_flags & IFF_802_1Q_VLAN) + return NOTIFY_DONE; + + /* Avoid Bonding master dev with same MAC registering as VF */ + if (event_dev->priv_flags & IFF_BONDING && + event_dev->flags & IFF_MASTER) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_REGISTER: + return netvsc_register_vf(event_dev); + case NETDEV_UNREGISTER: + return netvsc_unregister_vf(event_dev); + case NETDEV_UP: + return netvsc_vf_up(event_dev); + case NETDEV_DOWN: + return netvsc_vf_down(event_dev); + default: + return NOTIFY_DONE; + } +} + +static struct notifier_block netvsc_netdev_notifier = { + .notifier_call = netvsc_netdev_event, +}; + static void __exit netvsc_drv_exit(void) { + unregister_netdevice_notifier(&netvsc_netdev_notifier); vmbus_driver_unregister(&netvsc_drv); } static int __init netvsc_drv_init(void) { + int ret; + if (ring_size < RING_SIZE_MIN) { ring_size = RING_SIZE_MIN; pr_info("Increased ring_size to %d (min allowed)\n", ring_size); } - return vmbus_driver_register(&netvsc_drv); + ret = vmbus_driver_register(&netvsc_drv); + + if (ret) + return ret; + + register_netdevice_notifier(&netvsc_netdev_notifier); + return 0; } MODULE_LICENSE("GPL"); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index c4e1e0408..8e830f741 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -126,11 +126,7 @@ static void put_rndis_request(struct rndis_device *dev, static void dump_rndis_message(struct hv_device *hv_dev, struct rndis_message *rndis_msg) { - struct net_device *netdev; - struct netvsc_device *net_device; - - net_device = hv_get_drvdata(hv_dev); - netdev = net_device->ndev; + struct net_device *netdev = hv_get_drvdata(hv_dev); switch (rndis_msg->ndis_msg_type) { case RNDIS_MSG_PACKET: @@ -211,6 +207,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, struct hv_netvsc_packet *packet; struct hv_page_buffer page_buf[2]; struct hv_page_buffer *pb = page_buf; + struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); /* Setup the packet to send it */ packet = &req->pkt; @@ -236,7 +233,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, pb[0].len; } - ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL); + ret = netvsc_send(net_device_ctx->device_ctx, packet, NULL, &pb, NULL); return ret; } @@ -262,9 +259,7 @@ static void rndis_filter_receive_response(struct rndis_device *dev, struct rndis_request *request = NULL; bool found = false; unsigned long flags; - struct net_device *ndev; - - ndev = dev->net_dev->ndev; + struct net_device *ndev = dev->ndev; spin_lock_irqsave(&dev->request_lock, flags); list_for_each_entry(request, &dev->req_list, list_ent) { @@ -355,6 +350,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev, struct ndis_pkt_8021q_info *vlan; struct ndis_tcp_ip_checksum_info *csum_info; u16 vlan_tci = 0; + struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); rndis_pkt = &msg->msg.pkt; @@ -368,7 +364,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev, * should be the data packet size plus the trailer padding size */ if (pkt->total_data_buflen < rndis_pkt->data_len) { - netdev_err(dev->net_dev->ndev, "rndis message buffer " + netdev_err(dev->ndev, "rndis message buffer " "overflow detected (got %u, min %u)" "...dropping this message!\n", pkt->total_data_buflen, rndis_pkt->data_len); @@ -390,7 +386,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev, } csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); - return netvsc_recv_callback(dev->net_dev->dev, pkt, data, + return netvsc_recv_callback(net_device_ctx->device_ctx, pkt, data, csum_info, channel, vlan_tci); } @@ -399,10 +395,11 @@ int rndis_filter_receive(struct hv_device *dev, void **data, struct vmbus_channel *channel) { - struct netvsc_device *net_dev = hv_get_drvdata(dev); + struct net_device *ndev = hv_get_drvdata(dev); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_dev = net_device_ctx->nvdev; struct rndis_device *rndis_dev; struct rndis_message *rndis_msg; - struct net_device *ndev; int ret = 0; if (!net_dev) { @@ -410,8 +407,6 @@ int rndis_filter_receive(struct hv_device *dev, goto exit; } - ndev = net_dev->ndev; - /* Make sure the rndis device state is initialized */ if (!net_dev->extension) { netdev_err(ndev, "got rndis message but no rndis device - " @@ -430,7 +425,7 @@ int rndis_filter_receive(struct hv_device *dev, rndis_msg = *data; - if (netif_msg_rx_err(net_dev->nd_ctx)) + if (netif_msg_rx_err(net_device_ctx)) dump_rndis_message(dev, rndis_msg); switch (rndis_msg->ndis_msg_type) { @@ -471,7 +466,6 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, struct rndis_query_request *query; struct rndis_query_complete *query_complete; int ret = 0; - unsigned long t; if (!result) return -EINVAL; @@ -508,11 +502,7 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } + wait_for_completion(&request->wait_event); /* Copy the response back */ query_complete = &request->response_msg.msg.query_complete; @@ -548,11 +538,10 @@ static int rndis_filter_query_device_mac(struct rndis_device *dev) #define NWADR_STR "NetworkAddress" #define NWADR_STRLEN 14 -int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) +int rndis_filter_set_device_mac(struct net_device *ndev, char *mac) { - struct netvsc_device *nvdev = hv_get_drvdata(hdev); + struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev); struct rndis_device *rdev = nvdev->extension; - struct net_device *ndev = nvdev->ndev; struct rndis_request *request; struct rndis_set_request *set; struct rndis_config_parameter_info *cpi; @@ -562,7 +551,6 @@ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) u32 extlen = sizeof(struct rndis_config_parameter_info) + 2*NWADR_STRLEN + 4*ETH_ALEN; int ret; - unsigned long t; request = get_rndis_request(rdev, RNDIS_MSG_SET, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); @@ -603,21 +591,13 @@ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - netdev_err(ndev, "timeout before we got a set response...\n"); - /* - * can't put_rndis_request, since we may still receive a - * send-completion. - */ - return -EBUSY; - } else { - set_complete = &request->response_msg.msg.set_complete; - if (set_complete->status != RNDIS_STATUS_SUCCESS) { - netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", - set_complete->status); - ret = -EINVAL; - } + wait_for_completion(&request->wait_event); + + set_complete = &request->response_msg.msg.set_complete; + if (set_complete->status != RNDIS_STATUS_SUCCESS) { + netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", + set_complete->status); + ret = -EINVAL; } cleanup: @@ -626,19 +606,17 @@ cleanup: } static int -rndis_filter_set_offload_params(struct hv_device *hdev, +rndis_filter_set_offload_params(struct net_device *ndev, struct ndis_offload_params *req_offloads) { - struct netvsc_device *nvdev = hv_get_drvdata(hdev); + struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev); struct rndis_device *rdev = nvdev->extension; - struct net_device *ndev = nvdev->ndev; struct rndis_request *request; struct rndis_set_request *set; struct ndis_offload_params *offload_params; struct rndis_set_complete *set_complete; u32 extlen = sizeof(struct ndis_offload_params); int ret; - unsigned long t; u32 vsp_version = nvdev->nvsp_version; if (vsp_version <= NVSP_PROTOCOL_VERSION_4) { @@ -672,20 +650,12 @@ rndis_filter_set_offload_params(struct hv_device *hdev, if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - netdev_err(ndev, "timeout before we got aOFFLOAD set response...\n"); - /* can't put_rndis_request, since we may still receive a - * send-completion. - */ - return -EBUSY; - } else { - set_complete = &request->response_msg.msg.set_complete; - if (set_complete->status != RNDIS_STATUS_SUCCESS) { - netdev_err(ndev, "Fail to set offload on host side:0x%x\n", - set_complete->status); - ret = -EINVAL; - } + wait_for_completion(&request->wait_event); + set_complete = &request->response_msg.msg.set_complete; + if (set_complete->status != RNDIS_STATUS_SUCCESS) { + netdev_err(ndev, "Fail to set offload on host side:0x%x\n", + set_complete->status); + ret = -EINVAL; } cleanup: @@ -703,7 +673,7 @@ u8 netvsc_hash_key[HASH_KEYLEN] = { static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) { - struct net_device *ndev = rdev->net_dev->ndev; + struct net_device *ndev = rdev->ndev; struct rndis_request *request; struct rndis_set_request *set; struct rndis_set_complete *set_complete; @@ -713,7 +683,6 @@ static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) u32 *itab; u8 *keyp; int i, ret; - unsigned long t; request = get_rndis_request( rdev, RNDIS_MSG_SET, @@ -756,20 +725,12 @@ static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - netdev_err(ndev, "timeout before we got a set response...\n"); - /* can't put_rndis_request, since we may still receive a - * send-completion. - */ - return -ETIMEDOUT; - } else { - set_complete = &request->response_msg.msg.set_complete; - if (set_complete->status != RNDIS_STATUS_SUCCESS) { - netdev_err(ndev, "Fail to set RSS parameters:0x%x\n", - set_complete->status); - ret = -EINVAL; - } + wait_for_completion(&request->wait_event); + set_complete = &request->response_msg.msg.set_complete; + if (set_complete->status != RNDIS_STATUS_SUCCESS) { + netdev_err(ndev, "Fail to set RSS parameters:0x%x\n", + set_complete->status); + ret = -EINVAL; } cleanup: @@ -798,10 +759,6 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) struct rndis_set_complete *set_complete; u32 status; int ret; - unsigned long t; - struct net_device *ndev; - - ndev = dev->net_dev->ndev; request = get_rndis_request(dev, RNDIS_MSG_SET, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + @@ -824,26 +781,14 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + wait_for_completion(&request->wait_event); - if (t == 0) { - netdev_err(ndev, - "timeout before we got a set response...\n"); - ret = -ETIMEDOUT; - /* - * We can't deallocate the request since we may still receive a - * send completion for it. - */ - goto exit; - } else { - set_complete = &request->response_msg.msg.set_complete; - status = set_complete->status; - } + set_complete = &request->response_msg.msg.set_complete; + status = set_complete->status; cleanup: if (request) put_rndis_request(dev, request); -exit: return ret; } @@ -855,8 +800,7 @@ static int rndis_filter_init_device(struct rndis_device *dev) struct rndis_initialize_complete *init_complete; u32 status; int ret; - unsigned long t; - struct netvsc_device *nvdev = dev->net_dev; + struct netvsc_device *nvdev = net_device_to_netvsc_device(dev->ndev); request = get_rndis_request(dev, RNDIS_MSG_INIT, RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); @@ -879,13 +823,7 @@ static int rndis_filter_init_device(struct rndis_device *dev) goto cleanup; } - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } + wait_for_completion(&request->wait_event); init_complete = &request->response_msg.msg.init_complete; status = init_complete->status; @@ -910,8 +848,9 @@ static void rndis_filter_halt_device(struct rndis_device *dev) { struct rndis_request *request; struct rndis_halt_request *halt; - struct netvsc_device *nvdev = dev->net_dev; - struct hv_device *hdev = nvdev->dev; + struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; + struct hv_device *hdev = net_device_ctx->device_ctx; ulong flags; /* Attempt to do a rndis device halt */ @@ -979,13 +918,13 @@ static int rndis_filter_close_device(struct rndis_device *dev) static void netvsc_sc_open(struct vmbus_channel *new_sc) { - struct netvsc_device *nvscdev; + struct net_device *ndev = + hv_get_drvdata(new_sc->primary_channel->device_obj); + struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev); u16 chn_index = new_sc->offermsg.offer.sub_channel_index; int ret; unsigned long flags; - nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj); - if (chn_index >= nvscdev->num_chn) return; @@ -1010,12 +949,13 @@ int rndis_filter_device_add(struct hv_device *dev, void *additional_info) { int ret; + struct net_device *net = hv_get_drvdata(dev); + struct net_device_context *net_device_ctx = netdev_priv(net); struct netvsc_device *net_device; struct rndis_device *rndis_device; struct netvsc_device_info *device_info = additional_info; struct ndis_offload_params offloads; struct nvsp_message *init_packet; - unsigned long t; struct ndis_recv_scale_cap rsscap; u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); u32 mtu, size; @@ -1040,16 +980,15 @@ int rndis_filter_device_add(struct hv_device *dev, return ret; } - /* Initialize the rndis device */ - net_device = hv_get_drvdata(dev); + net_device = net_device_ctx->nvdev; net_device->max_chn = 1; net_device->num_chn = 1; spin_lock_init(&net_device->sc_lock); net_device->extension = rndis_device; - rndis_device->net_dev = net_device; + rndis_device->ndev = net; /* Send the rndis initialization message */ ret = rndis_filter_init_device(rndis_device); @@ -1063,8 +1002,8 @@ int rndis_filter_device_add(struct hv_device *dev, ret = rndis_filter_query_device(rndis_device, RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, &mtu, &size); - if (ret == 0 && size == sizeof(u32) && mtu < net_device->ndev->mtu) - net_device->ndev->mtu = mtu; + if (ret == 0 && size == sizeof(u32) && mtu < net->mtu) + net->mtu = mtu; /* Get the mac address */ ret = rndis_filter_query_device_mac(rndis_device); @@ -1090,7 +1029,7 @@ int rndis_filter_device_add(struct hv_device *dev, offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; - ret = rndis_filter_set_offload_params(dev, &offloads); + ret = rndis_filter_set_offload_params(net, &offloads); if (ret) goto err_dev_remv; @@ -1159,11 +1098,8 @@ int rndis_filter_device_add(struct hv_device *dev, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret) goto out; - t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto out; - } + wait_for_completion(&net_device->channel_init_wait); + if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) { ret = -ENODEV; @@ -1198,19 +1134,14 @@ err_dev_remv: void rndis_filter_device_remove(struct hv_device *dev) { - struct netvsc_device *net_dev = hv_get_drvdata(dev); + struct netvsc_device *net_dev = hv_device_to_netvsc_device(dev); struct rndis_device *rndis_dev = net_dev->extension; - unsigned long t; /* If not all subchannel offers are complete, wait for them until * completion to avoid race. */ - while (net_dev->num_sc_offered > 0) { - t = wait_for_completion_timeout(&net_dev->channel_init_wait, - 10 * HZ); - if (t == 0) - WARN(1, "Netvsc: Waiting for sub-channel processing"); - } + if (net_dev->num_sc_offered > 0) + wait_for_completion(&net_dev->channel_init_wait); /* Halt and release the rndis device */ rndis_filter_halt_device(rndis_dev); @@ -1222,22 +1153,24 @@ void rndis_filter_device_remove(struct hv_device *dev) } -int rndis_filter_open(struct hv_device *dev) +int rndis_filter_open(struct netvsc_device *nvdev) { - struct netvsc_device *net_device = hv_get_drvdata(dev); - - if (!net_device) + if (!nvdev) return -EINVAL; - return rndis_filter_open_device(net_device->extension); + if (atomic_inc_return(&nvdev->open_cnt) != 1) + return 0; + + return rndis_filter_open_device(nvdev->extension); } -int rndis_filter_close(struct hv_device *dev) +int rndis_filter_close(struct netvsc_device *nvdev) { - struct netvsc_device *nvdev = hv_get_drvdata(dev); - if (!nvdev) return -EINVAL; + if (atomic_dec_return(&nvdev->open_cnt) != 0) + return 0; + return rndis_filter_close_device(nvdev->extension); } |