195fa0405SHaiyang Zhang /* 295fa0405SHaiyang Zhang * Copyright (c) 2009, Microsoft Corporation. 395fa0405SHaiyang Zhang * 495fa0405SHaiyang Zhang * This program is free software; you can redistribute it and/or modify it 595fa0405SHaiyang Zhang * under the terms and conditions of the GNU General Public License, 695fa0405SHaiyang Zhang * version 2, as published by the Free Software Foundation. 795fa0405SHaiyang Zhang * 895fa0405SHaiyang Zhang * This program is distributed in the hope it will be useful, but WITHOUT 995fa0405SHaiyang Zhang * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1095fa0405SHaiyang Zhang * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1195fa0405SHaiyang Zhang * more details. 1295fa0405SHaiyang Zhang * 1395fa0405SHaiyang Zhang * You should have received a copy of the GNU General Public License along with 14adf8d3ffSJeff Kirsher * this program; if not, see <http://www.gnu.org/licenses/>. 1595fa0405SHaiyang Zhang * 1695fa0405SHaiyang Zhang * Authors: 1795fa0405SHaiyang Zhang * Haiyang Zhang <haiyangz@microsoft.com> 1895fa0405SHaiyang Zhang * Hank Janssen <hjanssen@microsoft.com> 1995fa0405SHaiyang Zhang */ 2095fa0405SHaiyang Zhang #include <linux/kernel.h> 2195fa0405SHaiyang Zhang #include <linux/sched.h> 2295fa0405SHaiyang Zhang #include <linux/wait.h> 2395fa0405SHaiyang Zhang #include <linux/highmem.h> 2495fa0405SHaiyang Zhang #include <linux/slab.h> 2595fa0405SHaiyang Zhang #include <linux/io.h> 2695fa0405SHaiyang Zhang #include <linux/if_ether.h> 2795fa0405SHaiyang Zhang #include <linux/netdevice.h> 281f5f3a75SHaiyang Zhang #include <linux/if_vlan.h> 291ce09e89SHaiyang Zhang #include <linux/nls.h> 30d6472302SStephen Rothwell #include <linux/vmalloc.h> 3195fa0405SHaiyang Zhang 3295fa0405SHaiyang Zhang #include "hyperv_net.h" 3395fa0405SHaiyang Zhang 3495fa0405SHaiyang Zhang 355b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE 3695fa0405SHaiyang Zhang struct rndis_request { 3795fa0405SHaiyang Zhang struct list_head list_ent; 3895fa0405SHaiyang Zhang struct completion wait_event; 3995fa0405SHaiyang Zhang 4095fa0405SHaiyang Zhang struct rndis_message response_msg; 41a3a6cab5SHaiyang Zhang /* 42a3a6cab5SHaiyang Zhang * The buffer for extended info after the RNDIS response message. It's 43a3a6cab5SHaiyang Zhang * referenced based on the data offset in the RNDIS message. Its size 44a3a6cab5SHaiyang Zhang * is enough for current needs, and should be sufficient for the near 45a3a6cab5SHaiyang Zhang * future. 46a3a6cab5SHaiyang Zhang */ 47a3a6cab5SHaiyang Zhang u8 response_ext[RNDIS_EXT_LEN]; 4895fa0405SHaiyang Zhang 4995fa0405SHaiyang Zhang /* Simplify allocation by having a netvsc packet inline */ 5095fa0405SHaiyang Zhang struct hv_netvsc_packet pkt; 510f48917bSHaiyang Zhang 5295fa0405SHaiyang Zhang struct rndis_message request_msg; 530f48917bSHaiyang Zhang /* 54a3a6cab5SHaiyang Zhang * The buffer for the extended info after the RNDIS request message. 55a3a6cab5SHaiyang Zhang * It is referenced and sized in a similar way as response_ext. 560f48917bSHaiyang Zhang */ 57a3a6cab5SHaiyang Zhang u8 request_ext[RNDIS_EXT_LEN]; 5895fa0405SHaiyang Zhang }; 5995fa0405SHaiyang Zhang 6095fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void) 6195fa0405SHaiyang Zhang { 6295fa0405SHaiyang Zhang struct rndis_device *device; 6395fa0405SHaiyang Zhang 6495fa0405SHaiyang Zhang device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL); 6595fa0405SHaiyang Zhang if (!device) 6695fa0405SHaiyang Zhang return NULL; 6795fa0405SHaiyang Zhang 6895fa0405SHaiyang Zhang spin_lock_init(&device->request_lock); 6995fa0405SHaiyang Zhang 7095fa0405SHaiyang Zhang INIT_LIST_HEAD(&device->req_list); 7195fa0405SHaiyang Zhang 7295fa0405SHaiyang Zhang device->state = RNDIS_DEV_UNINITIALIZED; 7395fa0405SHaiyang Zhang 7495fa0405SHaiyang Zhang return device; 7595fa0405SHaiyang Zhang } 7695fa0405SHaiyang Zhang 7795fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev, 7895fa0405SHaiyang Zhang u32 msg_type, 7995fa0405SHaiyang Zhang u32 msg_len) 8095fa0405SHaiyang Zhang { 8195fa0405SHaiyang Zhang struct rndis_request *request; 8295fa0405SHaiyang Zhang struct rndis_message *rndis_msg; 8395fa0405SHaiyang Zhang struct rndis_set_request *set; 8495fa0405SHaiyang Zhang unsigned long flags; 8595fa0405SHaiyang Zhang 8695fa0405SHaiyang Zhang request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL); 8795fa0405SHaiyang Zhang if (!request) 8895fa0405SHaiyang Zhang return NULL; 8995fa0405SHaiyang Zhang 9095fa0405SHaiyang Zhang init_completion(&request->wait_event); 9195fa0405SHaiyang Zhang 9295fa0405SHaiyang Zhang rndis_msg = &request->request_msg; 9395fa0405SHaiyang Zhang rndis_msg->ndis_msg_type = msg_type; 9495fa0405SHaiyang Zhang rndis_msg->msg_len = msg_len; 9595fa0405SHaiyang Zhang 965b54dac8SHaiyang Zhang request->pkt.q_idx = 0; 975b54dac8SHaiyang Zhang 9895fa0405SHaiyang Zhang /* 9995fa0405SHaiyang Zhang * Set the request id. This field is always after the rndis header for 10095fa0405SHaiyang Zhang * request/response packet types so we just used the SetRequest as a 10195fa0405SHaiyang Zhang * template 10295fa0405SHaiyang Zhang */ 10395fa0405SHaiyang Zhang set = &rndis_msg->msg.set_req; 10495fa0405SHaiyang Zhang set->req_id = atomic_inc_return(&dev->new_req_id); 10595fa0405SHaiyang Zhang 10695fa0405SHaiyang Zhang /* Add to the request list */ 10795fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 10895fa0405SHaiyang Zhang list_add_tail(&request->list_ent, &dev->req_list); 10995fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 11095fa0405SHaiyang Zhang 11195fa0405SHaiyang Zhang return request; 11295fa0405SHaiyang Zhang } 11395fa0405SHaiyang Zhang 11495fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev, 11595fa0405SHaiyang Zhang struct rndis_request *req) 11695fa0405SHaiyang Zhang { 11795fa0405SHaiyang Zhang unsigned long flags; 11895fa0405SHaiyang Zhang 11995fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 12095fa0405SHaiyang Zhang list_del(&req->list_ent); 12195fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 12295fa0405SHaiyang Zhang 12395fa0405SHaiyang Zhang kfree(req); 12495fa0405SHaiyang Zhang } 12595fa0405SHaiyang Zhang 12695fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev, 12795fa0405SHaiyang Zhang struct rndis_message *rndis_msg) 12895fa0405SHaiyang Zhang { 1293d541ac5SVitaly Kuznetsov struct net_device *netdev = hv_get_drvdata(hv_dev); 13095fa0405SHaiyang Zhang 13195fa0405SHaiyang Zhang switch (rndis_msg->ndis_msg_type) { 13251491167SLinus Walleij case RNDIS_MSG_PACKET: 13351491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, " 13495fa0405SHaiyang Zhang "data offset %u data len %u, # oob %u, " 13595fa0405SHaiyang Zhang "oob offset %u, oob len %u, pkt offset %u, " 13695fa0405SHaiyang Zhang "pkt len %u\n", 13795fa0405SHaiyang Zhang rndis_msg->msg_len, 13895fa0405SHaiyang Zhang rndis_msg->msg.pkt.data_offset, 13995fa0405SHaiyang Zhang rndis_msg->msg.pkt.data_len, 14095fa0405SHaiyang Zhang rndis_msg->msg.pkt.num_oob_data_elements, 14195fa0405SHaiyang Zhang rndis_msg->msg.pkt.oob_data_offset, 14295fa0405SHaiyang Zhang rndis_msg->msg.pkt.oob_data_len, 14395fa0405SHaiyang Zhang rndis_msg->msg.pkt.per_pkt_info_offset, 14495fa0405SHaiyang Zhang rndis_msg->msg.pkt.per_pkt_info_len); 14595fa0405SHaiyang Zhang break; 14695fa0405SHaiyang Zhang 14751491167SLinus Walleij case RNDIS_MSG_INIT_C: 14851491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_INIT_C " 14995fa0405SHaiyang Zhang "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " 15095fa0405SHaiyang Zhang "device flags %d, max xfer size 0x%x, max pkts %u, " 15195fa0405SHaiyang Zhang "pkt aligned %u)\n", 15295fa0405SHaiyang Zhang rndis_msg->msg_len, 15395fa0405SHaiyang Zhang rndis_msg->msg.init_complete.req_id, 15495fa0405SHaiyang Zhang rndis_msg->msg.init_complete.status, 15595fa0405SHaiyang Zhang rndis_msg->msg.init_complete.major_ver, 15695fa0405SHaiyang Zhang rndis_msg->msg.init_complete.minor_ver, 15795fa0405SHaiyang Zhang rndis_msg->msg.init_complete.dev_flags, 15895fa0405SHaiyang Zhang rndis_msg->msg.init_complete.max_xfer_size, 15995fa0405SHaiyang Zhang rndis_msg->msg.init_complete. 16095fa0405SHaiyang Zhang max_pkt_per_msg, 16195fa0405SHaiyang Zhang rndis_msg->msg.init_complete. 16295fa0405SHaiyang Zhang pkt_alignment_factor); 16395fa0405SHaiyang Zhang break; 16495fa0405SHaiyang Zhang 16551491167SLinus Walleij case RNDIS_MSG_QUERY_C: 16651491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_QUERY_C " 16795fa0405SHaiyang Zhang "(len %u, id 0x%x, status 0x%x, buf len %u, " 16895fa0405SHaiyang Zhang "buf offset %u)\n", 16995fa0405SHaiyang Zhang rndis_msg->msg_len, 17095fa0405SHaiyang Zhang rndis_msg->msg.query_complete.req_id, 17195fa0405SHaiyang Zhang rndis_msg->msg.query_complete.status, 17295fa0405SHaiyang Zhang rndis_msg->msg.query_complete. 17395fa0405SHaiyang Zhang info_buflen, 17495fa0405SHaiyang Zhang rndis_msg->msg.query_complete. 17595fa0405SHaiyang Zhang info_buf_offset); 17695fa0405SHaiyang Zhang break; 17795fa0405SHaiyang Zhang 17851491167SLinus Walleij case RNDIS_MSG_SET_C: 17995fa0405SHaiyang Zhang netdev_dbg(netdev, 18051491167SLinus Walleij "RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n", 18195fa0405SHaiyang Zhang rndis_msg->msg_len, 18295fa0405SHaiyang Zhang rndis_msg->msg.set_complete.req_id, 18395fa0405SHaiyang Zhang rndis_msg->msg.set_complete.status); 18495fa0405SHaiyang Zhang break; 18595fa0405SHaiyang Zhang 18651491167SLinus Walleij case RNDIS_MSG_INDICATE: 18751491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_INDICATE " 18895fa0405SHaiyang Zhang "(len %u, status 0x%x, buf len %u, buf offset %u)\n", 18995fa0405SHaiyang Zhang rndis_msg->msg_len, 19095fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status, 19195fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status_buflen, 19295fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status_buf_offset); 19395fa0405SHaiyang Zhang break; 19495fa0405SHaiyang Zhang 19595fa0405SHaiyang Zhang default: 19695fa0405SHaiyang Zhang netdev_dbg(netdev, "0x%x (len %u)\n", 19795fa0405SHaiyang Zhang rndis_msg->ndis_msg_type, 19895fa0405SHaiyang Zhang rndis_msg->msg_len); 19995fa0405SHaiyang Zhang break; 20095fa0405SHaiyang Zhang } 20195fa0405SHaiyang Zhang } 20295fa0405SHaiyang Zhang 20395fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev, 20495fa0405SHaiyang Zhang struct rndis_request *req) 20595fa0405SHaiyang Zhang { 20695fa0405SHaiyang Zhang int ret; 20795fa0405SHaiyang Zhang struct hv_netvsc_packet *packet; 208b08cc791SKY Srinivasan struct hv_page_buffer page_buf[2]; 209a9f2e2d6SKY Srinivasan struct hv_page_buffer *pb = page_buf; 2103d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); 21195fa0405SHaiyang Zhang 21295fa0405SHaiyang Zhang /* Setup the packet to send it */ 21395fa0405SHaiyang Zhang packet = &req->pkt; 21495fa0405SHaiyang Zhang 21595fa0405SHaiyang Zhang packet->total_data_buflen = req->request_msg.msg_len; 21695fa0405SHaiyang Zhang packet->page_buf_cnt = 1; 21795fa0405SHaiyang Zhang 218a9f2e2d6SKY Srinivasan pb[0].pfn = virt_to_phys(&req->request_msg) >> 21995fa0405SHaiyang Zhang PAGE_SHIFT; 220a9f2e2d6SKY Srinivasan pb[0].len = req->request_msg.msg_len; 221a9f2e2d6SKY Srinivasan pb[0].offset = 22295fa0405SHaiyang Zhang (unsigned long)&req->request_msg & (PAGE_SIZE - 1); 22395fa0405SHaiyang Zhang 22499e3fcfaSHaiyang Zhang /* Add one page_buf when request_msg crossing page boundary */ 225a9f2e2d6SKY Srinivasan if (pb[0].offset + pb[0].len > PAGE_SIZE) { 22699e3fcfaSHaiyang Zhang packet->page_buf_cnt++; 227a9f2e2d6SKY Srinivasan pb[0].len = PAGE_SIZE - 228a9f2e2d6SKY Srinivasan pb[0].offset; 229a9f2e2d6SKY Srinivasan pb[1].pfn = virt_to_phys((void *)&req->request_msg 230a9f2e2d6SKY Srinivasan + pb[0].len) >> PAGE_SHIFT; 231a9f2e2d6SKY Srinivasan pb[1].offset = 0; 232a9f2e2d6SKY Srinivasan pb[1].len = req->request_msg.msg_len - 233a9f2e2d6SKY Srinivasan pb[0].len; 23499e3fcfaSHaiyang Zhang } 23599e3fcfaSHaiyang Zhang 2363d541ac5SVitaly Kuznetsov ret = netvsc_send(net_device_ctx->device_ctx, packet, NULL, &pb, NULL); 23795fa0405SHaiyang Zhang return ret; 23895fa0405SHaiyang Zhang } 23995fa0405SHaiyang Zhang 2401b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev, 2411b07da51SHaiyang Zhang struct rndis_request *request) 2421b07da51SHaiyang Zhang { 2431b07da51SHaiyang Zhang u32 link_status; 2441b07da51SHaiyang Zhang struct rndis_query_complete *query_complete; 2451b07da51SHaiyang Zhang 2461b07da51SHaiyang Zhang query_complete = &request->response_msg.msg.query_complete; 2471b07da51SHaiyang Zhang 2481b07da51SHaiyang Zhang if (query_complete->status == RNDIS_STATUS_SUCCESS && 2491b07da51SHaiyang Zhang query_complete->info_buflen == sizeof(u32)) { 2501b07da51SHaiyang Zhang memcpy(&link_status, (void *)((unsigned long)query_complete + 2511b07da51SHaiyang Zhang query_complete->info_buf_offset), sizeof(u32)); 2521b07da51SHaiyang Zhang rdev->link_state = link_status != 0; 2531b07da51SHaiyang Zhang } 2541b07da51SHaiyang Zhang } 2551b07da51SHaiyang Zhang 25695fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev, 25795fa0405SHaiyang Zhang struct rndis_message *resp) 25895fa0405SHaiyang Zhang { 25995fa0405SHaiyang Zhang struct rndis_request *request = NULL; 26095fa0405SHaiyang Zhang bool found = false; 26195fa0405SHaiyang Zhang unsigned long flags; 2623d541ac5SVitaly Kuznetsov struct net_device *ndev = dev->ndev; 26395fa0405SHaiyang Zhang 26495fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 26595fa0405SHaiyang Zhang list_for_each_entry(request, &dev->req_list, list_ent) { 26695fa0405SHaiyang Zhang /* 26795fa0405SHaiyang Zhang * All request/response message contains RequestId as the 1st 26895fa0405SHaiyang Zhang * field 26995fa0405SHaiyang Zhang */ 27095fa0405SHaiyang Zhang if (request->request_msg.msg.init_req.req_id 27195fa0405SHaiyang Zhang == resp->msg.init_complete.req_id) { 27295fa0405SHaiyang Zhang found = true; 27395fa0405SHaiyang Zhang break; 27495fa0405SHaiyang Zhang } 27595fa0405SHaiyang Zhang } 27695fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 27795fa0405SHaiyang Zhang 27895fa0405SHaiyang Zhang if (found) { 279a3a6cab5SHaiyang Zhang if (resp->msg_len <= 280a3a6cab5SHaiyang Zhang sizeof(struct rndis_message) + RNDIS_EXT_LEN) { 28195fa0405SHaiyang Zhang memcpy(&request->response_msg, resp, 28295fa0405SHaiyang Zhang resp->msg_len); 2831b07da51SHaiyang Zhang if (request->request_msg.ndis_msg_type == 2841b07da51SHaiyang Zhang RNDIS_MSG_QUERY && request->request_msg.msg. 2851b07da51SHaiyang Zhang query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS) 2861b07da51SHaiyang Zhang rndis_set_link_state(dev, request); 28795fa0405SHaiyang Zhang } else { 28895fa0405SHaiyang Zhang netdev_err(ndev, 28995fa0405SHaiyang Zhang "rndis response buffer overflow " 29095fa0405SHaiyang Zhang "detected (size %u max %zu)\n", 29195fa0405SHaiyang Zhang resp->msg_len, 29286eedaccSKY Srinivasan sizeof(struct rndis_message)); 29395fa0405SHaiyang Zhang 29495fa0405SHaiyang Zhang if (resp->ndis_msg_type == 29551491167SLinus Walleij RNDIS_MSG_RESET_C) { 29695fa0405SHaiyang Zhang /* does not have a request id field */ 29795fa0405SHaiyang Zhang request->response_msg.msg.reset_complete. 298007e5c8eSLinus Walleij status = RNDIS_STATUS_BUFFER_OVERFLOW; 29995fa0405SHaiyang Zhang } else { 30095fa0405SHaiyang Zhang request->response_msg.msg. 30195fa0405SHaiyang Zhang init_complete.status = 302007e5c8eSLinus Walleij RNDIS_STATUS_BUFFER_OVERFLOW; 30395fa0405SHaiyang Zhang } 30495fa0405SHaiyang Zhang } 30595fa0405SHaiyang Zhang 30695fa0405SHaiyang Zhang complete(&request->wait_event); 30795fa0405SHaiyang Zhang } else { 30895fa0405SHaiyang Zhang netdev_err(ndev, 30995fa0405SHaiyang Zhang "no rndis request found for this response " 31095fa0405SHaiyang Zhang "(id 0x%x res type 0x%x)\n", 31195fa0405SHaiyang Zhang resp->msg.init_complete.req_id, 31295fa0405SHaiyang Zhang resp->ndis_msg_type); 31395fa0405SHaiyang Zhang } 31495fa0405SHaiyang Zhang } 31595fa0405SHaiyang Zhang 3161f5f3a75SHaiyang Zhang /* 3171f5f3a75SHaiyang Zhang * Get the Per-Packet-Info with the specified type 3181f5f3a75SHaiyang Zhang * return NULL if not found. 3191f5f3a75SHaiyang Zhang */ 3201f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) 3211f5f3a75SHaiyang Zhang { 3221f5f3a75SHaiyang Zhang struct rndis_per_packet_info *ppi; 3231f5f3a75SHaiyang Zhang int len; 3241f5f3a75SHaiyang Zhang 3251f5f3a75SHaiyang Zhang if (rpkt->per_pkt_info_offset == 0) 3261f5f3a75SHaiyang Zhang return NULL; 3271f5f3a75SHaiyang Zhang 3281f5f3a75SHaiyang Zhang ppi = (struct rndis_per_packet_info *)((ulong)rpkt + 3291f5f3a75SHaiyang Zhang rpkt->per_pkt_info_offset); 3301f5f3a75SHaiyang Zhang len = rpkt->per_pkt_info_len; 3311f5f3a75SHaiyang Zhang 3321f5f3a75SHaiyang Zhang while (len > 0) { 3331f5f3a75SHaiyang Zhang if (ppi->type == type) 3341f5f3a75SHaiyang Zhang return (void *)((ulong)ppi + ppi->ppi_offset); 3351f5f3a75SHaiyang Zhang len -= ppi->size; 3361f5f3a75SHaiyang Zhang ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size); 3371f5f3a75SHaiyang Zhang } 3381f5f3a75SHaiyang Zhang 3391f5f3a75SHaiyang Zhang return NULL; 3401f5f3a75SHaiyang Zhang } 3411f5f3a75SHaiyang Zhang 34210082f98SKY Srinivasan static int rndis_filter_receive_data(struct rndis_device *dev, 34395fa0405SHaiyang Zhang struct rndis_message *msg, 34425b85ee8SKY Srinivasan struct hv_netvsc_packet *pkt, 345c4b20c63SKY Srinivasan void **data, 34625b85ee8SKY Srinivasan struct vmbus_channel *channel) 34795fa0405SHaiyang Zhang { 34895fa0405SHaiyang Zhang struct rndis_packet *rndis_pkt; 34995fa0405SHaiyang Zhang u32 data_offset; 3501f5f3a75SHaiyang Zhang struct ndis_pkt_8021q_info *vlan; 351e3d605edSKY Srinivasan struct ndis_tcp_ip_checksum_info *csum_info; 352760d1e36SKY Srinivasan u16 vlan_tci = 0; 3533d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); 35495fa0405SHaiyang Zhang 35595fa0405SHaiyang Zhang rndis_pkt = &msg->msg.pkt; 35695fa0405SHaiyang Zhang 35795fa0405SHaiyang Zhang /* Remove the rndis header and pass it back up the stack */ 35895fa0405SHaiyang Zhang data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; 35995fa0405SHaiyang Zhang 36095fa0405SHaiyang Zhang pkt->total_data_buflen -= data_offset; 3614b8a8bc9SWei Yongjun 3624b8a8bc9SWei Yongjun /* 3634b8a8bc9SWei Yongjun * Make sure we got a valid RNDIS message, now total_data_buflen 3644b8a8bc9SWei Yongjun * should be the data packet size plus the trailer padding size 3654b8a8bc9SWei Yongjun */ 3664b8a8bc9SWei Yongjun if (pkt->total_data_buflen < rndis_pkt->data_len) { 3673d541ac5SVitaly Kuznetsov netdev_err(dev->ndev, "rndis message buffer " 3684b8a8bc9SWei Yongjun "overflow detected (got %u, min %u)" 3694b8a8bc9SWei Yongjun "...dropping this message!\n", 3704b8a8bc9SWei Yongjun pkt->total_data_buflen, rndis_pkt->data_len); 37110082f98SKY Srinivasan return NVSP_STAT_FAIL; 3724b8a8bc9SWei Yongjun } 3734b8a8bc9SWei Yongjun 3744b8a8bc9SWei Yongjun /* 3754b8a8bc9SWei Yongjun * Remove the rndis trailer padding from rndis packet message 3764b8a8bc9SWei Yongjun * rndis_pkt->data_len tell us the real data length, we only copy 3774b8a8bc9SWei Yongjun * the data packet to the stack, without the rndis trailer padding 3784b8a8bc9SWei Yongjun */ 3794b8a8bc9SWei Yongjun pkt->total_data_buflen = rndis_pkt->data_len; 380c4b20c63SKY Srinivasan *data = (void *)((unsigned long)(*data) + data_offset); 38195fa0405SHaiyang Zhang 3821f5f3a75SHaiyang Zhang vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); 3831f5f3a75SHaiyang Zhang if (vlan) { 384760d1e36SKY Srinivasan vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid | 3851f5f3a75SHaiyang Zhang (vlan->pri << VLAN_PRIO_SHIFT); 3861f5f3a75SHaiyang Zhang } 3871f5f3a75SHaiyang Zhang 388e3d605edSKY Srinivasan csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); 3893d541ac5SVitaly Kuznetsov return netvsc_recv_callback(net_device_ctx->device_ctx, pkt, data, 390760d1e36SKY Srinivasan csum_info, channel, vlan_tci); 39195fa0405SHaiyang Zhang } 39295fa0405SHaiyang Zhang 39395fa0405SHaiyang Zhang int rndis_filter_receive(struct hv_device *dev, 39425b85ee8SKY Srinivasan struct hv_netvsc_packet *pkt, 395c4b20c63SKY Srinivasan void **data, 39625b85ee8SKY Srinivasan struct vmbus_channel *channel) 39795fa0405SHaiyang Zhang { 3983d541ac5SVitaly Kuznetsov struct net_device *ndev = hv_get_drvdata(dev); 3993d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(ndev); 4003d541ac5SVitaly Kuznetsov struct netvsc_device *net_dev = net_device_ctx->nvdev; 40195fa0405SHaiyang Zhang struct rndis_device *rndis_dev; 402ef31bef6SHaiyang Zhang struct rndis_message *rndis_msg; 40363f6921dSHaiyang Zhang int ret = 0; 40495fa0405SHaiyang Zhang 40563f6921dSHaiyang Zhang if (!net_dev) { 40610082f98SKY Srinivasan ret = NVSP_STAT_FAIL; 40763f6921dSHaiyang Zhang goto exit; 40863f6921dSHaiyang Zhang } 40995fa0405SHaiyang Zhang 41095fa0405SHaiyang Zhang /* Make sure the rndis device state is initialized */ 41195fa0405SHaiyang Zhang if (!net_dev->extension) { 41295fa0405SHaiyang Zhang netdev_err(ndev, "got rndis message but no rndis device - " 41395fa0405SHaiyang Zhang "dropping this message!\n"); 41410082f98SKY Srinivasan ret = NVSP_STAT_FAIL; 41563f6921dSHaiyang Zhang goto exit; 41695fa0405SHaiyang Zhang } 41795fa0405SHaiyang Zhang 41895fa0405SHaiyang Zhang rndis_dev = (struct rndis_device *)net_dev->extension; 41995fa0405SHaiyang Zhang if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { 42095fa0405SHaiyang Zhang netdev_err(ndev, "got rndis message but rndis device " 42195fa0405SHaiyang Zhang "uninitialized...dropping this message!\n"); 42210082f98SKY Srinivasan ret = NVSP_STAT_FAIL; 42363f6921dSHaiyang Zhang goto exit; 42495fa0405SHaiyang Zhang } 42595fa0405SHaiyang Zhang 426c4b20c63SKY Srinivasan rndis_msg = *data; 42795fa0405SHaiyang Zhang 4283d541ac5SVitaly Kuznetsov if (netif_msg_rx_err(net_device_ctx)) 429ef31bef6SHaiyang Zhang dump_rndis_message(dev, rndis_msg); 43095fa0405SHaiyang Zhang 431ef31bef6SHaiyang Zhang switch (rndis_msg->ndis_msg_type) { 43251491167SLinus Walleij case RNDIS_MSG_PACKET: 43395fa0405SHaiyang Zhang /* data msg */ 43410082f98SKY Srinivasan ret = rndis_filter_receive_data(rndis_dev, rndis_msg, pkt, 435c4b20c63SKY Srinivasan data, channel); 43695fa0405SHaiyang Zhang break; 43795fa0405SHaiyang Zhang 43851491167SLinus Walleij case RNDIS_MSG_INIT_C: 43951491167SLinus Walleij case RNDIS_MSG_QUERY_C: 44051491167SLinus Walleij case RNDIS_MSG_SET_C: 44195fa0405SHaiyang Zhang /* completion msgs */ 442ef31bef6SHaiyang Zhang rndis_filter_receive_response(rndis_dev, rndis_msg); 44395fa0405SHaiyang Zhang break; 44495fa0405SHaiyang Zhang 44551491167SLinus Walleij case RNDIS_MSG_INDICATE: 44695fa0405SHaiyang Zhang /* notification msgs */ 4473a494e71SHaiyang Zhang netvsc_linkstatus_callback(dev, rndis_msg); 44895fa0405SHaiyang Zhang break; 44995fa0405SHaiyang Zhang default: 45095fa0405SHaiyang Zhang netdev_err(ndev, 45195fa0405SHaiyang Zhang "unhandled rndis message (type %u len %u)\n", 452ef31bef6SHaiyang Zhang rndis_msg->ndis_msg_type, 453ef31bef6SHaiyang Zhang rndis_msg->msg_len); 45495fa0405SHaiyang Zhang break; 45595fa0405SHaiyang Zhang } 45695fa0405SHaiyang Zhang 45763f6921dSHaiyang Zhang exit: 45863f6921dSHaiyang Zhang return ret; 45995fa0405SHaiyang Zhang } 46095fa0405SHaiyang Zhang 46195fa0405SHaiyang Zhang static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, 46295fa0405SHaiyang Zhang void *result, u32 *result_size) 46395fa0405SHaiyang Zhang { 46495fa0405SHaiyang Zhang struct rndis_request *request; 46595fa0405SHaiyang Zhang u32 inresult_size = *result_size; 46695fa0405SHaiyang Zhang struct rndis_query_request *query; 46795fa0405SHaiyang Zhang struct rndis_query_complete *query_complete; 46895fa0405SHaiyang Zhang int ret = 0; 469999028ccSNicholas Mc Guire unsigned long t; 47095fa0405SHaiyang Zhang 47195fa0405SHaiyang Zhang if (!result) 47295fa0405SHaiyang Zhang return -EINVAL; 47395fa0405SHaiyang Zhang 47495fa0405SHaiyang Zhang *result_size = 0; 47551491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_QUERY, 47695fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_query_request)); 47795fa0405SHaiyang Zhang if (!request) { 47895fa0405SHaiyang Zhang ret = -ENOMEM; 47995fa0405SHaiyang Zhang goto cleanup; 48095fa0405SHaiyang Zhang } 48195fa0405SHaiyang Zhang 48295fa0405SHaiyang Zhang /* Setup the rndis query */ 48395fa0405SHaiyang Zhang query = &request->request_msg.msg.query_req; 48495fa0405SHaiyang Zhang query->oid = oid; 48595fa0405SHaiyang Zhang query->info_buf_offset = sizeof(struct rndis_query_request); 48695fa0405SHaiyang Zhang query->info_buflen = 0; 48795fa0405SHaiyang Zhang query->dev_vc_handle = 0; 48895fa0405SHaiyang Zhang 4895b54dac8SHaiyang Zhang if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) { 4905b54dac8SHaiyang Zhang struct ndis_recv_scale_cap *cap; 4915b54dac8SHaiyang Zhang 4925b54dac8SHaiyang Zhang request->request_msg.msg_len += 4935b54dac8SHaiyang Zhang sizeof(struct ndis_recv_scale_cap); 4945b54dac8SHaiyang Zhang query->info_buflen = sizeof(struct ndis_recv_scale_cap); 4955b54dac8SHaiyang Zhang cap = (struct ndis_recv_scale_cap *)((unsigned long)query + 4965b54dac8SHaiyang Zhang query->info_buf_offset); 4975b54dac8SHaiyang Zhang cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES; 4985b54dac8SHaiyang Zhang cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2; 4995b54dac8SHaiyang Zhang cap->hdr.size = sizeof(struct ndis_recv_scale_cap); 5005b54dac8SHaiyang Zhang } 5015b54dac8SHaiyang Zhang 50295fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 50395fa0405SHaiyang Zhang if (ret != 0) 50495fa0405SHaiyang Zhang goto cleanup; 50595fa0405SHaiyang Zhang 50695fa0405SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 50795fa0405SHaiyang Zhang if (t == 0) { 50895fa0405SHaiyang Zhang ret = -ETIMEDOUT; 50995fa0405SHaiyang Zhang goto cleanup; 51095fa0405SHaiyang Zhang } 51195fa0405SHaiyang Zhang 51295fa0405SHaiyang Zhang /* Copy the response back */ 51395fa0405SHaiyang Zhang query_complete = &request->response_msg.msg.query_complete; 51495fa0405SHaiyang Zhang 51595fa0405SHaiyang Zhang if (query_complete->info_buflen > inresult_size) { 51695fa0405SHaiyang Zhang ret = -1; 51795fa0405SHaiyang Zhang goto cleanup; 51895fa0405SHaiyang Zhang } 51995fa0405SHaiyang Zhang 52095fa0405SHaiyang Zhang memcpy(result, 52195fa0405SHaiyang Zhang (void *)((unsigned long)query_complete + 52295fa0405SHaiyang Zhang query_complete->info_buf_offset), 52395fa0405SHaiyang Zhang query_complete->info_buflen); 52495fa0405SHaiyang Zhang 52595fa0405SHaiyang Zhang *result_size = query_complete->info_buflen; 52695fa0405SHaiyang Zhang 52795fa0405SHaiyang Zhang cleanup: 52895fa0405SHaiyang Zhang if (request) 52995fa0405SHaiyang Zhang put_rndis_request(dev, request); 53095fa0405SHaiyang Zhang 53195fa0405SHaiyang Zhang return ret; 53295fa0405SHaiyang Zhang } 53395fa0405SHaiyang Zhang 53495fa0405SHaiyang Zhang static int rndis_filter_query_device_mac(struct rndis_device *dev) 53595fa0405SHaiyang Zhang { 53695fa0405SHaiyang Zhang u32 size = ETH_ALEN; 53795fa0405SHaiyang Zhang 53895fa0405SHaiyang Zhang return rndis_filter_query_device(dev, 53995fa0405SHaiyang Zhang RNDIS_OID_802_3_PERMANENT_ADDRESS, 54095fa0405SHaiyang Zhang dev->hw_mac_adr, &size); 54195fa0405SHaiyang Zhang } 54295fa0405SHaiyang Zhang 5431ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress" 5441ce09e89SHaiyang Zhang #define NWADR_STRLEN 14 5451ce09e89SHaiyang Zhang 5461ce09e89SHaiyang Zhang int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) 5471ce09e89SHaiyang Zhang { 5483d541ac5SVitaly Kuznetsov struct net_device *ndev = hv_get_drvdata(hdev); 5492625466dSVitaly Kuznetsov struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev); 5501ce09e89SHaiyang Zhang struct rndis_device *rdev = nvdev->extension; 5511ce09e89SHaiyang Zhang struct rndis_request *request; 5521ce09e89SHaiyang Zhang struct rndis_set_request *set; 5531ce09e89SHaiyang Zhang struct rndis_config_parameter_info *cpi; 5541ce09e89SHaiyang Zhang wchar_t *cfg_nwadr, *cfg_mac; 5551ce09e89SHaiyang Zhang struct rndis_set_complete *set_complete; 5561ce09e89SHaiyang Zhang char macstr[2*ETH_ALEN+1]; 5571ce09e89SHaiyang Zhang u32 extlen = sizeof(struct rndis_config_parameter_info) + 5581ce09e89SHaiyang Zhang 2*NWADR_STRLEN + 4*ETH_ALEN; 559999028ccSNicholas Mc Guire int ret; 560999028ccSNicholas Mc Guire unsigned long t; 5611ce09e89SHaiyang Zhang 5621ce09e89SHaiyang Zhang request = get_rndis_request(rdev, RNDIS_MSG_SET, 5631ce09e89SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 5641ce09e89SHaiyang Zhang if (!request) 5651ce09e89SHaiyang Zhang return -ENOMEM; 5661ce09e89SHaiyang Zhang 5671ce09e89SHaiyang Zhang set = &request->request_msg.msg.set_req; 5681ce09e89SHaiyang Zhang set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER; 5691ce09e89SHaiyang Zhang set->info_buflen = extlen; 5701ce09e89SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 5711ce09e89SHaiyang Zhang set->dev_vc_handle = 0; 5721ce09e89SHaiyang Zhang 5731ce09e89SHaiyang Zhang cpi = (struct rndis_config_parameter_info *)((ulong)set + 5741ce09e89SHaiyang Zhang set->info_buf_offset); 5751ce09e89SHaiyang Zhang cpi->parameter_name_offset = 5761ce09e89SHaiyang Zhang sizeof(struct rndis_config_parameter_info); 5771ce09e89SHaiyang Zhang /* Multiply by 2 because host needs 2 bytes (utf16) for each char */ 5781ce09e89SHaiyang Zhang cpi->parameter_name_length = 2*NWADR_STRLEN; 5791ce09e89SHaiyang Zhang cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING; 5801ce09e89SHaiyang Zhang cpi->parameter_value_offset = 5811ce09e89SHaiyang Zhang cpi->parameter_name_offset + cpi->parameter_name_length; 5821ce09e89SHaiyang Zhang /* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */ 5831ce09e89SHaiyang Zhang cpi->parameter_value_length = 4*ETH_ALEN; 5841ce09e89SHaiyang Zhang 5851ce09e89SHaiyang Zhang cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset); 5861ce09e89SHaiyang Zhang cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset); 5871ce09e89SHaiyang Zhang ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN, 5881ce09e89SHaiyang Zhang cfg_nwadr, NWADR_STRLEN); 5891ce09e89SHaiyang Zhang if (ret < 0) 5901ce09e89SHaiyang Zhang goto cleanup; 5911ce09e89SHaiyang Zhang snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac); 5921ce09e89SHaiyang Zhang ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN, 5931ce09e89SHaiyang Zhang cfg_mac, 2*ETH_ALEN); 5941ce09e89SHaiyang Zhang if (ret < 0) 5951ce09e89SHaiyang Zhang goto cleanup; 5961ce09e89SHaiyang Zhang 5971ce09e89SHaiyang Zhang ret = rndis_filter_send_request(rdev, request); 5981ce09e89SHaiyang Zhang if (ret != 0) 5991ce09e89SHaiyang Zhang goto cleanup; 6001ce09e89SHaiyang Zhang 6011ce09e89SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 6021ce09e89SHaiyang Zhang if (t == 0) { 6031ce09e89SHaiyang Zhang netdev_err(ndev, "timeout before we got a set response...\n"); 6041ce09e89SHaiyang Zhang /* 6051ce09e89SHaiyang Zhang * can't put_rndis_request, since we may still receive a 6061ce09e89SHaiyang Zhang * send-completion. 6071ce09e89SHaiyang Zhang */ 6081ce09e89SHaiyang Zhang return -EBUSY; 6091ce09e89SHaiyang Zhang } else { 6101ce09e89SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 611b02a8067SHaiyang Zhang if (set_complete->status != RNDIS_STATUS_SUCCESS) { 612b02a8067SHaiyang Zhang netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", 613b02a8067SHaiyang Zhang set_complete->status); 6141ce09e89SHaiyang Zhang ret = -EINVAL; 6151ce09e89SHaiyang Zhang } 616b02a8067SHaiyang Zhang } 6171ce09e89SHaiyang Zhang 6181ce09e89SHaiyang Zhang cleanup: 6191ce09e89SHaiyang Zhang put_rndis_request(rdev, request); 6201ce09e89SHaiyang Zhang return ret; 6211ce09e89SHaiyang Zhang } 6221ce09e89SHaiyang Zhang 623da19fcd0SLad, Prabhakar static int 624da19fcd0SLad, Prabhakar rndis_filter_set_offload_params(struct hv_device *hdev, 6254a0e70aeSKY Srinivasan struct ndis_offload_params *req_offloads) 6264a0e70aeSKY Srinivasan { 6273d541ac5SVitaly Kuznetsov struct net_device *ndev = hv_get_drvdata(hdev); 6282625466dSVitaly Kuznetsov struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev); 6294a0e70aeSKY Srinivasan struct rndis_device *rdev = nvdev->extension; 6304a0e70aeSKY Srinivasan struct rndis_request *request; 6314a0e70aeSKY Srinivasan struct rndis_set_request *set; 6324a0e70aeSKY Srinivasan struct ndis_offload_params *offload_params; 6334a0e70aeSKY Srinivasan struct rndis_set_complete *set_complete; 6344a0e70aeSKY Srinivasan u32 extlen = sizeof(struct ndis_offload_params); 635999028ccSNicholas Mc Guire int ret; 636999028ccSNicholas Mc Guire unsigned long t; 637af9893a3SKY Srinivasan u32 vsp_version = nvdev->nvsp_version; 638af9893a3SKY Srinivasan 639af9893a3SKY Srinivasan if (vsp_version <= NVSP_PROTOCOL_VERSION_4) { 640af9893a3SKY Srinivasan extlen = VERSION_4_OFFLOAD_SIZE; 641af9893a3SKY Srinivasan /* On NVSP_PROTOCOL_VERSION_4 and below, we do not support 642af9893a3SKY Srinivasan * UDP checksum offload. 643af9893a3SKY Srinivasan */ 644af9893a3SKY Srinivasan req_offloads->udp_ip_v4_csum = 0; 645af9893a3SKY Srinivasan req_offloads->udp_ip_v6_csum = 0; 646af9893a3SKY Srinivasan } 6474a0e70aeSKY Srinivasan 6484a0e70aeSKY Srinivasan request = get_rndis_request(rdev, RNDIS_MSG_SET, 6494a0e70aeSKY Srinivasan RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 6504a0e70aeSKY Srinivasan if (!request) 6514a0e70aeSKY Srinivasan return -ENOMEM; 6524a0e70aeSKY Srinivasan 6534a0e70aeSKY Srinivasan set = &request->request_msg.msg.set_req; 6544a0e70aeSKY Srinivasan set->oid = OID_TCP_OFFLOAD_PARAMETERS; 6554a0e70aeSKY Srinivasan set->info_buflen = extlen; 6564a0e70aeSKY Srinivasan set->info_buf_offset = sizeof(struct rndis_set_request); 6574a0e70aeSKY Srinivasan set->dev_vc_handle = 0; 6584a0e70aeSKY Srinivasan 6594a0e70aeSKY Srinivasan offload_params = (struct ndis_offload_params *)((ulong)set + 6604a0e70aeSKY Srinivasan set->info_buf_offset); 6614a0e70aeSKY Srinivasan *offload_params = *req_offloads; 6624a0e70aeSKY Srinivasan offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT; 6634a0e70aeSKY Srinivasan offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3; 6644a0e70aeSKY Srinivasan offload_params->header.size = extlen; 6654a0e70aeSKY Srinivasan 6664a0e70aeSKY Srinivasan ret = rndis_filter_send_request(rdev, request); 6674a0e70aeSKY Srinivasan if (ret != 0) 6684a0e70aeSKY Srinivasan goto cleanup; 6694a0e70aeSKY Srinivasan 6704a0e70aeSKY Srinivasan t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 6714a0e70aeSKY Srinivasan if (t == 0) { 6724a0e70aeSKY Srinivasan netdev_err(ndev, "timeout before we got aOFFLOAD set response...\n"); 6734a0e70aeSKY Srinivasan /* can't put_rndis_request, since we may still receive a 6744a0e70aeSKY Srinivasan * send-completion. 6754a0e70aeSKY Srinivasan */ 6764a0e70aeSKY Srinivasan return -EBUSY; 6774a0e70aeSKY Srinivasan } else { 6784a0e70aeSKY Srinivasan set_complete = &request->response_msg.msg.set_complete; 6794a0e70aeSKY Srinivasan if (set_complete->status != RNDIS_STATUS_SUCCESS) { 680af9893a3SKY Srinivasan netdev_err(ndev, "Fail to set offload on host side:0x%x\n", 6814a0e70aeSKY Srinivasan set_complete->status); 6824a0e70aeSKY Srinivasan ret = -EINVAL; 6834a0e70aeSKY Srinivasan } 6844a0e70aeSKY Srinivasan } 6854a0e70aeSKY Srinivasan 6864a0e70aeSKY Srinivasan cleanup: 6874a0e70aeSKY Srinivasan put_rndis_request(rdev, request); 6884a0e70aeSKY Srinivasan return ret; 6894a0e70aeSKY Srinivasan } 6901ce09e89SHaiyang Zhang 6915b54dac8SHaiyang Zhang u8 netvsc_hash_key[HASH_KEYLEN] = { 6925b54dac8SHaiyang Zhang 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 6935b54dac8SHaiyang Zhang 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 6945b54dac8SHaiyang Zhang 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, 6955b54dac8SHaiyang Zhang 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, 6965b54dac8SHaiyang Zhang 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa 6975b54dac8SHaiyang Zhang }; 6985b54dac8SHaiyang Zhang 699da19fcd0SLad, Prabhakar static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) 7005b54dac8SHaiyang Zhang { 7013d541ac5SVitaly Kuznetsov struct net_device *ndev = rdev->ndev; 7025b54dac8SHaiyang Zhang struct rndis_request *request; 7035b54dac8SHaiyang Zhang struct rndis_set_request *set; 7045b54dac8SHaiyang Zhang struct rndis_set_complete *set_complete; 7055b54dac8SHaiyang Zhang u32 extlen = sizeof(struct ndis_recv_scale_param) + 7065b54dac8SHaiyang Zhang 4*ITAB_NUM + HASH_KEYLEN; 7075b54dac8SHaiyang Zhang struct ndis_recv_scale_param *rssp; 7085b54dac8SHaiyang Zhang u32 *itab; 7095b54dac8SHaiyang Zhang u8 *keyp; 710999028ccSNicholas Mc Guire int i, ret; 711999028ccSNicholas Mc Guire unsigned long t; 7125b54dac8SHaiyang Zhang 7135b54dac8SHaiyang Zhang request = get_rndis_request( 7145b54dac8SHaiyang Zhang rdev, RNDIS_MSG_SET, 7155b54dac8SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 7165b54dac8SHaiyang Zhang if (!request) 7175b54dac8SHaiyang Zhang return -ENOMEM; 7185b54dac8SHaiyang Zhang 7195b54dac8SHaiyang Zhang set = &request->request_msg.msg.set_req; 7205b54dac8SHaiyang Zhang set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS; 7215b54dac8SHaiyang Zhang set->info_buflen = extlen; 7225b54dac8SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 7235b54dac8SHaiyang Zhang set->dev_vc_handle = 0; 7245b54dac8SHaiyang Zhang 7255b54dac8SHaiyang Zhang rssp = (struct ndis_recv_scale_param *)(set + 1); 7265b54dac8SHaiyang Zhang rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS; 7275b54dac8SHaiyang Zhang rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2; 7285b54dac8SHaiyang Zhang rssp->hdr.size = sizeof(struct ndis_recv_scale_param); 7295b54dac8SHaiyang Zhang rssp->flag = 0; 7305b54dac8SHaiyang Zhang rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 | 7314c87454aSHaiyang Zhang NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 | 7324c87454aSHaiyang Zhang NDIS_HASH_TCP_IPV6; 7335b54dac8SHaiyang Zhang rssp->indirect_tabsize = 4*ITAB_NUM; 7345b54dac8SHaiyang Zhang rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param); 7355b54dac8SHaiyang Zhang rssp->hashkey_size = HASH_KEYLEN; 7365b54dac8SHaiyang Zhang rssp->kashkey_offset = rssp->indirect_taboffset + 7375b54dac8SHaiyang Zhang rssp->indirect_tabsize; 7385b54dac8SHaiyang Zhang 7395b54dac8SHaiyang Zhang /* Set indirection table entries */ 7405b54dac8SHaiyang Zhang itab = (u32 *)(rssp + 1); 7415b54dac8SHaiyang Zhang for (i = 0; i < ITAB_NUM; i++) 7425b54dac8SHaiyang Zhang itab[i] = i % num_queue; 7435b54dac8SHaiyang Zhang 7445b54dac8SHaiyang Zhang /* Set hask key values */ 7455b54dac8SHaiyang Zhang keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset); 7465b54dac8SHaiyang Zhang for (i = 0; i < HASH_KEYLEN; i++) 7475b54dac8SHaiyang Zhang keyp[i] = netvsc_hash_key[i]; 7485b54dac8SHaiyang Zhang 7495b54dac8SHaiyang Zhang 7505b54dac8SHaiyang Zhang ret = rndis_filter_send_request(rdev, request); 7515b54dac8SHaiyang Zhang if (ret != 0) 7525b54dac8SHaiyang Zhang goto cleanup; 7535b54dac8SHaiyang Zhang 7545b54dac8SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 7555b54dac8SHaiyang Zhang if (t == 0) { 7565b54dac8SHaiyang Zhang netdev_err(ndev, "timeout before we got a set response...\n"); 7575b54dac8SHaiyang Zhang /* can't put_rndis_request, since we may still receive a 7585b54dac8SHaiyang Zhang * send-completion. 7595b54dac8SHaiyang Zhang */ 7605b54dac8SHaiyang Zhang return -ETIMEDOUT; 7615b54dac8SHaiyang Zhang } else { 7625b54dac8SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 7635b54dac8SHaiyang Zhang if (set_complete->status != RNDIS_STATUS_SUCCESS) { 7645b54dac8SHaiyang Zhang netdev_err(ndev, "Fail to set RSS parameters:0x%x\n", 7655b54dac8SHaiyang Zhang set_complete->status); 7665b54dac8SHaiyang Zhang ret = -EINVAL; 7675b54dac8SHaiyang Zhang } 7685b54dac8SHaiyang Zhang } 7695b54dac8SHaiyang Zhang 7705b54dac8SHaiyang Zhang cleanup: 7715b54dac8SHaiyang Zhang put_rndis_request(rdev, request); 7725b54dac8SHaiyang Zhang return ret; 7735b54dac8SHaiyang Zhang } 7745b54dac8SHaiyang Zhang 7755b54dac8SHaiyang Zhang 77695fa0405SHaiyang Zhang static int rndis_filter_query_device_link_status(struct rndis_device *dev) 77795fa0405SHaiyang Zhang { 77895fa0405SHaiyang Zhang u32 size = sizeof(u32); 77995fa0405SHaiyang Zhang u32 link_status; 78095fa0405SHaiyang Zhang int ret; 78195fa0405SHaiyang Zhang 78295fa0405SHaiyang Zhang ret = rndis_filter_query_device(dev, 78395fa0405SHaiyang Zhang RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, 78495fa0405SHaiyang Zhang &link_status, &size); 78595fa0405SHaiyang Zhang 78695fa0405SHaiyang Zhang return ret; 78795fa0405SHaiyang Zhang } 78895fa0405SHaiyang Zhang 789d426b2e3SHaiyang Zhang int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) 79095fa0405SHaiyang Zhang { 79195fa0405SHaiyang Zhang struct rndis_request *request; 79295fa0405SHaiyang Zhang struct rndis_set_request *set; 79395fa0405SHaiyang Zhang struct rndis_set_complete *set_complete; 79495fa0405SHaiyang Zhang u32 status; 795999028ccSNicholas Mc Guire int ret; 796999028ccSNicholas Mc Guire unsigned long t; 7973d541ac5SVitaly Kuznetsov struct net_device *ndev = dev->ndev; 79895fa0405SHaiyang Zhang 79951491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_SET, 80095fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + 80195fa0405SHaiyang Zhang sizeof(u32)); 80295fa0405SHaiyang Zhang if (!request) { 80395fa0405SHaiyang Zhang ret = -ENOMEM; 80495fa0405SHaiyang Zhang goto cleanup; 80595fa0405SHaiyang Zhang } 80695fa0405SHaiyang Zhang 80795fa0405SHaiyang Zhang /* Setup the rndis set */ 80895fa0405SHaiyang Zhang set = &request->request_msg.msg.set_req; 80995fa0405SHaiyang Zhang set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; 81095fa0405SHaiyang Zhang set->info_buflen = sizeof(u32); 81195fa0405SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 81295fa0405SHaiyang Zhang 81395fa0405SHaiyang Zhang memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), 81495fa0405SHaiyang Zhang &new_filter, sizeof(u32)); 81595fa0405SHaiyang Zhang 81695fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 81795fa0405SHaiyang Zhang if (ret != 0) 81895fa0405SHaiyang Zhang goto cleanup; 81995fa0405SHaiyang Zhang 82095fa0405SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 82195fa0405SHaiyang Zhang 82295fa0405SHaiyang Zhang if (t == 0) { 82395fa0405SHaiyang Zhang netdev_err(ndev, 82495fa0405SHaiyang Zhang "timeout before we got a set response...\n"); 825ea496374SHaiyang Zhang ret = -ETIMEDOUT; 82695fa0405SHaiyang Zhang /* 82795fa0405SHaiyang Zhang * We can't deallocate the request since we may still receive a 82895fa0405SHaiyang Zhang * send completion for it. 82995fa0405SHaiyang Zhang */ 83095fa0405SHaiyang Zhang goto exit; 83195fa0405SHaiyang Zhang } else { 83295fa0405SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 83395fa0405SHaiyang Zhang status = set_complete->status; 83495fa0405SHaiyang Zhang } 83595fa0405SHaiyang Zhang 83695fa0405SHaiyang Zhang cleanup: 83795fa0405SHaiyang Zhang if (request) 83895fa0405SHaiyang Zhang put_rndis_request(dev, request); 83995fa0405SHaiyang Zhang exit: 84095fa0405SHaiyang Zhang return ret; 84195fa0405SHaiyang Zhang } 84295fa0405SHaiyang Zhang 84395fa0405SHaiyang Zhang 84495fa0405SHaiyang Zhang static int rndis_filter_init_device(struct rndis_device *dev) 84595fa0405SHaiyang Zhang { 84695fa0405SHaiyang Zhang struct rndis_request *request; 84795fa0405SHaiyang Zhang struct rndis_initialize_request *init; 84895fa0405SHaiyang Zhang struct rndis_initialize_complete *init_complete; 84995fa0405SHaiyang Zhang u32 status; 850999028ccSNicholas Mc Guire int ret; 851999028ccSNicholas Mc Guire unsigned long t; 8522625466dSVitaly Kuznetsov struct netvsc_device *nvdev = net_device_to_netvsc_device(dev->ndev); 85395fa0405SHaiyang Zhang 85451491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_INIT, 85595fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); 85695fa0405SHaiyang Zhang if (!request) { 85795fa0405SHaiyang Zhang ret = -ENOMEM; 85895fa0405SHaiyang Zhang goto cleanup; 85995fa0405SHaiyang Zhang } 86095fa0405SHaiyang Zhang 86195fa0405SHaiyang Zhang /* Setup the rndis set */ 86295fa0405SHaiyang Zhang init = &request->request_msg.msg.init_req; 86395fa0405SHaiyang Zhang init->major_ver = RNDIS_MAJOR_VERSION; 86495fa0405SHaiyang Zhang init->minor_ver = RNDIS_MINOR_VERSION; 865fb1d074eSHaiyang Zhang init->max_xfer_size = 0x4000; 86695fa0405SHaiyang Zhang 86795fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZING; 86895fa0405SHaiyang Zhang 86995fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 87095fa0405SHaiyang Zhang if (ret != 0) { 87195fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 87295fa0405SHaiyang Zhang goto cleanup; 87395fa0405SHaiyang Zhang } 87495fa0405SHaiyang Zhang 87595fa0405SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 87695fa0405SHaiyang Zhang 87795fa0405SHaiyang Zhang if (t == 0) { 87895fa0405SHaiyang Zhang ret = -ETIMEDOUT; 87995fa0405SHaiyang Zhang goto cleanup; 88095fa0405SHaiyang Zhang } 88195fa0405SHaiyang Zhang 88295fa0405SHaiyang Zhang init_complete = &request->response_msg.msg.init_complete; 88395fa0405SHaiyang Zhang status = init_complete->status; 88495fa0405SHaiyang Zhang if (status == RNDIS_STATUS_SUCCESS) { 88595fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZED; 8867c3877f2SHaiyang Zhang nvdev->max_pkt = init_complete->max_pkt_per_msg; 8877c3877f2SHaiyang Zhang nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor; 88895fa0405SHaiyang Zhang ret = 0; 88995fa0405SHaiyang Zhang } else { 89095fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 89195fa0405SHaiyang Zhang ret = -EINVAL; 89295fa0405SHaiyang Zhang } 89395fa0405SHaiyang Zhang 89495fa0405SHaiyang Zhang cleanup: 89595fa0405SHaiyang Zhang if (request) 89695fa0405SHaiyang Zhang put_rndis_request(dev, request); 89795fa0405SHaiyang Zhang 89895fa0405SHaiyang Zhang return ret; 89995fa0405SHaiyang Zhang } 90095fa0405SHaiyang Zhang 90195fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev) 90295fa0405SHaiyang Zhang { 90395fa0405SHaiyang Zhang struct rndis_request *request; 90495fa0405SHaiyang Zhang struct rndis_halt_request *halt; 9053d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); 9063d541ac5SVitaly Kuznetsov struct netvsc_device *nvdev = net_device_ctx->nvdev; 9073d541ac5SVitaly Kuznetsov struct hv_device *hdev = net_device_ctx->device_ctx; 908ae9e63bbSHaiyang Zhang ulong flags; 90995fa0405SHaiyang Zhang 91095fa0405SHaiyang Zhang /* Attempt to do a rndis device halt */ 91151491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_HALT, 91295fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); 91395fa0405SHaiyang Zhang if (!request) 91495fa0405SHaiyang Zhang goto cleanup; 91595fa0405SHaiyang Zhang 91695fa0405SHaiyang Zhang /* Setup the rndis set */ 91795fa0405SHaiyang Zhang halt = &request->request_msg.msg.halt_req; 91895fa0405SHaiyang Zhang halt->req_id = atomic_inc_return(&dev->new_req_id); 91995fa0405SHaiyang Zhang 92095fa0405SHaiyang Zhang /* Ignore return since this msg is optional. */ 92195fa0405SHaiyang Zhang rndis_filter_send_request(dev, request); 92295fa0405SHaiyang Zhang 92395fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 92495fa0405SHaiyang Zhang 92595fa0405SHaiyang Zhang cleanup: 926ae9e63bbSHaiyang Zhang spin_lock_irqsave(&hdev->channel->inbound_lock, flags); 927ae9e63bbSHaiyang Zhang nvdev->destroy = true; 928ae9e63bbSHaiyang Zhang spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags); 929ae9e63bbSHaiyang Zhang 930ae9e63bbSHaiyang Zhang /* Wait for all send completions */ 931ae9e63bbSHaiyang Zhang wait_event(nvdev->wait_drain, 932ae9e63bbSHaiyang Zhang atomic_read(&nvdev->num_outstanding_sends) == 0); 933ae9e63bbSHaiyang Zhang 93495fa0405SHaiyang Zhang if (request) 93595fa0405SHaiyang Zhang put_rndis_request(dev, request); 93695fa0405SHaiyang Zhang return; 93795fa0405SHaiyang Zhang } 93895fa0405SHaiyang Zhang 93995fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev) 94095fa0405SHaiyang Zhang { 94195fa0405SHaiyang Zhang int ret; 94295fa0405SHaiyang Zhang 94395fa0405SHaiyang Zhang if (dev->state != RNDIS_DEV_INITIALIZED) 94495fa0405SHaiyang Zhang return 0; 94595fa0405SHaiyang Zhang 94695fa0405SHaiyang Zhang ret = rndis_filter_set_packet_filter(dev, 94795fa0405SHaiyang Zhang NDIS_PACKET_TYPE_BROADCAST | 94895fa0405SHaiyang Zhang NDIS_PACKET_TYPE_ALL_MULTICAST | 94995fa0405SHaiyang Zhang NDIS_PACKET_TYPE_DIRECTED); 95095fa0405SHaiyang Zhang if (ret == 0) 95195fa0405SHaiyang Zhang dev->state = RNDIS_DEV_DATAINITIALIZED; 95295fa0405SHaiyang Zhang 95395fa0405SHaiyang Zhang return ret; 95495fa0405SHaiyang Zhang } 95595fa0405SHaiyang Zhang 95695fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev) 95795fa0405SHaiyang Zhang { 95895fa0405SHaiyang Zhang int ret; 95995fa0405SHaiyang Zhang 96095fa0405SHaiyang Zhang if (dev->state != RNDIS_DEV_DATAINITIALIZED) 96195fa0405SHaiyang Zhang return 0; 96295fa0405SHaiyang Zhang 96395fa0405SHaiyang Zhang ret = rndis_filter_set_packet_filter(dev, 0); 964c3582a2cSHaiyang Zhang if (ret == -ENODEV) 965c3582a2cSHaiyang Zhang ret = 0; 966c3582a2cSHaiyang Zhang 96795fa0405SHaiyang Zhang if (ret == 0) 96895fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZED; 96995fa0405SHaiyang Zhang 97095fa0405SHaiyang Zhang return ret; 97195fa0405SHaiyang Zhang } 97295fa0405SHaiyang Zhang 9735b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc) 9745b54dac8SHaiyang Zhang { 9753d541ac5SVitaly Kuznetsov struct net_device *ndev = 9763d541ac5SVitaly Kuznetsov hv_get_drvdata(new_sc->primary_channel->device_obj); 9772625466dSVitaly Kuznetsov struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev); 9785b54dac8SHaiyang Zhang u16 chn_index = new_sc->offermsg.offer.sub_channel_index; 9795b54dac8SHaiyang Zhang int ret; 980b3e6b82aSKY Srinivasan unsigned long flags; 9815b54dac8SHaiyang Zhang 9825b54dac8SHaiyang Zhang if (chn_index >= nvscdev->num_chn) 9835b54dac8SHaiyang Zhang return; 9845b54dac8SHaiyang Zhang 9855b54dac8SHaiyang Zhang set_per_channel_state(new_sc, nvscdev->sub_cb_buf + (chn_index - 1) * 9865b54dac8SHaiyang Zhang NETVSC_PACKET_SIZE); 9875b54dac8SHaiyang Zhang 9885b54dac8SHaiyang Zhang ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE, 9895b54dac8SHaiyang Zhang nvscdev->ring_size * PAGE_SIZE, NULL, 0, 9905b54dac8SHaiyang Zhang netvsc_channel_cb, new_sc); 9915b54dac8SHaiyang Zhang 9925b54dac8SHaiyang Zhang if (ret == 0) 9935b54dac8SHaiyang Zhang nvscdev->chn_table[chn_index] = new_sc; 9943f735131SHaiyang Zhang 9953f735131SHaiyang Zhang spin_lock_irqsave(&nvscdev->sc_lock, flags); 9963f735131SHaiyang Zhang nvscdev->num_sc_offered--; 9973f735131SHaiyang Zhang spin_unlock_irqrestore(&nvscdev->sc_lock, flags); 9983f735131SHaiyang Zhang if (nvscdev->num_sc_offered == 0) 9993f735131SHaiyang Zhang complete(&nvscdev->channel_init_wait); 10005b54dac8SHaiyang Zhang } 10015b54dac8SHaiyang Zhang 100295fa0405SHaiyang Zhang int rndis_filter_device_add(struct hv_device *dev, 100395fa0405SHaiyang Zhang void *additional_info) 100495fa0405SHaiyang Zhang { 100595fa0405SHaiyang Zhang int ret; 10063d541ac5SVitaly Kuznetsov struct net_device *net = hv_get_drvdata(dev); 10073d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(net); 100895fa0405SHaiyang Zhang struct netvsc_device *net_device; 100995fa0405SHaiyang Zhang struct rndis_device *rndis_device; 101095fa0405SHaiyang Zhang struct netvsc_device_info *device_info = additional_info; 10114a0e70aeSKY Srinivasan struct ndis_offload_params offloads; 10125b54dac8SHaiyang Zhang struct nvsp_message *init_packet; 1013999028ccSNicholas Mc Guire unsigned long t; 10145b54dac8SHaiyang Zhang struct ndis_recv_scale_cap rsscap; 10155b54dac8SHaiyang Zhang u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); 10164d3c9d37SHaiyang Zhang u32 mtu, size; 1017e01ec219SKY Srinivasan u32 num_rss_qs; 1018b3e6b82aSKY Srinivasan u32 sc_delta; 1019e01ec219SKY Srinivasan const struct cpumask *node_cpu_mask; 1020e01ec219SKY Srinivasan u32 num_possible_rss_qs; 1021b3e6b82aSKY Srinivasan unsigned long flags; 102295fa0405SHaiyang Zhang 102395fa0405SHaiyang Zhang rndis_device = get_rndis_device(); 102495fa0405SHaiyang Zhang if (!rndis_device) 102595fa0405SHaiyang Zhang return -ENODEV; 102695fa0405SHaiyang Zhang 102795fa0405SHaiyang Zhang /* 102895fa0405SHaiyang Zhang * Let the inner driver handle this first to create the netvsc channel 102995fa0405SHaiyang Zhang * NOTE! Once the channel is created, we may get a receive callback 103095fa0405SHaiyang Zhang * (RndisFilterOnReceive()) before this call is completed 103195fa0405SHaiyang Zhang */ 103295fa0405SHaiyang Zhang ret = netvsc_device_add(dev, additional_info); 103395fa0405SHaiyang Zhang if (ret != 0) { 103495fa0405SHaiyang Zhang kfree(rndis_device); 103595fa0405SHaiyang Zhang return ret; 103695fa0405SHaiyang Zhang } 103795fa0405SHaiyang Zhang 103895fa0405SHaiyang Zhang /* Initialize the rndis device */ 10393d541ac5SVitaly Kuznetsov net_device = net_device_ctx->nvdev; 104059995370SAndrew Schwartzmeyer net_device->max_chn = 1; 10415b54dac8SHaiyang Zhang net_device->num_chn = 1; 104295fa0405SHaiyang Zhang 1043b3e6b82aSKY Srinivasan spin_lock_init(&net_device->sc_lock); 1044b3e6b82aSKY Srinivasan 104595fa0405SHaiyang Zhang net_device->extension = rndis_device; 10463d541ac5SVitaly Kuznetsov rndis_device->ndev = net; 104795fa0405SHaiyang Zhang 104895fa0405SHaiyang Zhang /* Send the rndis initialization message */ 104995fa0405SHaiyang Zhang ret = rndis_filter_init_device(rndis_device); 105095fa0405SHaiyang Zhang if (ret != 0) { 10515243e7bdSHaiyang Zhang rndis_filter_device_remove(dev); 10525243e7bdSHaiyang Zhang return ret; 105395fa0405SHaiyang Zhang } 105495fa0405SHaiyang Zhang 10554d3c9d37SHaiyang Zhang /* Get the MTU from the host */ 10564d3c9d37SHaiyang Zhang size = sizeof(u32); 10574d3c9d37SHaiyang Zhang ret = rndis_filter_query_device(rndis_device, 10584d3c9d37SHaiyang Zhang RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, 10594d3c9d37SHaiyang Zhang &mtu, &size); 10600a1275caSVitaly Kuznetsov if (ret == 0 && size == sizeof(u32) && mtu < net->mtu) 10610a1275caSVitaly Kuznetsov net->mtu = mtu; 10624d3c9d37SHaiyang Zhang 106395fa0405SHaiyang Zhang /* Get the mac address */ 106495fa0405SHaiyang Zhang ret = rndis_filter_query_device_mac(rndis_device); 106595fa0405SHaiyang Zhang if (ret != 0) { 10665243e7bdSHaiyang Zhang rndis_filter_device_remove(dev); 10675243e7bdSHaiyang Zhang return ret; 106895fa0405SHaiyang Zhang } 106995fa0405SHaiyang Zhang 107095fa0405SHaiyang Zhang memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); 107195fa0405SHaiyang Zhang 10724a0e70aeSKY Srinivasan /* Turn on the offloads; the host supports all of the relevant 10734a0e70aeSKY Srinivasan * offloads. 10744a0e70aeSKY Srinivasan */ 10754a0e70aeSKY Srinivasan memset(&offloads, 0, sizeof(struct ndis_offload_params)); 10764a0e70aeSKY Srinivasan /* A value of zero means "no change"; now turn on what we 10774a0e70aeSKY Srinivasan * want. 10784a0e70aeSKY Srinivasan */ 10794a0e70aeSKY Srinivasan offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 10804a0e70aeSKY Srinivasan offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 10814a0e70aeSKY Srinivasan offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 10824a0e70aeSKY Srinivasan offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 10834a0e70aeSKY Srinivasan offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 10844a0e70aeSKY Srinivasan offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; 10854a0e70aeSKY Srinivasan 10864a0e70aeSKY Srinivasan 10874a0e70aeSKY Srinivasan ret = rndis_filter_set_offload_params(dev, &offloads); 10884a0e70aeSKY Srinivasan if (ret) 10894a0e70aeSKY Srinivasan goto err_dev_remv; 10904a0e70aeSKY Srinivasan 109195fa0405SHaiyang Zhang rndis_filter_query_device_link_status(rndis_device); 109295fa0405SHaiyang Zhang 109395fa0405SHaiyang Zhang device_info->link_state = rndis_device->link_state; 109495fa0405SHaiyang Zhang 109595fa0405SHaiyang Zhang dev_info(&dev->device, "Device MAC %pM link state %s\n", 109695fa0405SHaiyang Zhang rndis_device->hw_mac_adr, 109795fa0405SHaiyang Zhang device_info->link_state ? "down" : "up"); 109895fa0405SHaiyang Zhang 10995b54dac8SHaiyang Zhang if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5) 11005b54dac8SHaiyang Zhang return 0; 11015b54dac8SHaiyang Zhang 11025b54dac8SHaiyang Zhang /* vRSS setup */ 11035b54dac8SHaiyang Zhang memset(&rsscap, 0, rsscap_size); 11045b54dac8SHaiyang Zhang ret = rndis_filter_query_device(rndis_device, 11055b54dac8SHaiyang Zhang OID_GEN_RECEIVE_SCALE_CAPABILITIES, 11065b54dac8SHaiyang Zhang &rsscap, &rsscap_size); 11075b54dac8SHaiyang Zhang if (ret || rsscap.num_recv_que < 2) 11085b54dac8SHaiyang Zhang goto out; 11095b54dac8SHaiyang Zhang 11109efc2f7dSHaiyang Zhang net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, rsscap.num_recv_que); 1111e01ec219SKY Srinivasan 11129efc2f7dSHaiyang Zhang num_rss_qs = min(device_info->max_num_vrss_chns, net_device->max_chn); 1113e01ec219SKY Srinivasan 1114e01ec219SKY Srinivasan /* 1115e01ec219SKY Srinivasan * We will limit the VRSS channels to the number CPUs in the NUMA node 1116e01ec219SKY Srinivasan * the primary channel is currently bound to. 1117e01ec219SKY Srinivasan */ 1118e01ec219SKY Srinivasan node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu)); 1119e01ec219SKY Srinivasan num_possible_rss_qs = cpumask_weight(node_cpu_mask); 11208ebdcc52SAndrew Schwartzmeyer 11218ebdcc52SAndrew Schwartzmeyer /* We will use the given number of channels if available. */ 11228ebdcc52SAndrew Schwartzmeyer if (device_info->num_chn && device_info->num_chn < net_device->max_chn) 11238ebdcc52SAndrew Schwartzmeyer net_device->num_chn = device_info->num_chn; 11248ebdcc52SAndrew Schwartzmeyer else 1125e01ec219SKY Srinivasan net_device->num_chn = min(num_possible_rss_qs, num_rss_qs); 1126e01ec219SKY Srinivasan 1127b3e6b82aSKY Srinivasan num_rss_qs = net_device->num_chn - 1; 1128b3e6b82aSKY Srinivasan net_device->num_sc_offered = num_rss_qs; 1129b3e6b82aSKY Srinivasan 11305b54dac8SHaiyang Zhang if (net_device->num_chn == 1) 11315b54dac8SHaiyang Zhang goto out; 11325b54dac8SHaiyang Zhang 11335b54dac8SHaiyang Zhang net_device->sub_cb_buf = vzalloc((net_device->num_chn - 1) * 11345b54dac8SHaiyang Zhang NETVSC_PACKET_SIZE); 11355b54dac8SHaiyang Zhang if (!net_device->sub_cb_buf) { 11365b54dac8SHaiyang Zhang net_device->num_chn = 1; 11375b54dac8SHaiyang Zhang dev_info(&dev->device, "No memory for subchannels.\n"); 11385b54dac8SHaiyang Zhang goto out; 11395b54dac8SHaiyang Zhang } 11405b54dac8SHaiyang Zhang 11415b54dac8SHaiyang Zhang vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); 11425b54dac8SHaiyang Zhang 11435b54dac8SHaiyang Zhang init_packet = &net_device->channel_init_pkt; 11445b54dac8SHaiyang Zhang memset(init_packet, 0, sizeof(struct nvsp_message)); 11455b54dac8SHaiyang Zhang init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL; 11465b54dac8SHaiyang Zhang init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE; 11475b54dac8SHaiyang Zhang init_packet->msg.v5_msg.subchn_req.num_subchannels = 11485b54dac8SHaiyang Zhang net_device->num_chn - 1; 11495b54dac8SHaiyang Zhang ret = vmbus_sendpacket(dev->channel, init_packet, 11505b54dac8SHaiyang Zhang sizeof(struct nvsp_message), 11515b54dac8SHaiyang Zhang (unsigned long)init_packet, 11525b54dac8SHaiyang Zhang VM_PKT_DATA_INBAND, 11535b54dac8SHaiyang Zhang VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 11545b54dac8SHaiyang Zhang if (ret) 11555b54dac8SHaiyang Zhang goto out; 11565b54dac8SHaiyang Zhang t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); 11575b54dac8SHaiyang Zhang if (t == 0) { 11585b54dac8SHaiyang Zhang ret = -ETIMEDOUT; 11595b54dac8SHaiyang Zhang goto out; 11605b54dac8SHaiyang Zhang } 11615b54dac8SHaiyang Zhang if (init_packet->msg.v5_msg.subchn_comp.status != 11625b54dac8SHaiyang Zhang NVSP_STAT_SUCCESS) { 11635b54dac8SHaiyang Zhang ret = -ENODEV; 11645b54dac8SHaiyang Zhang goto out; 11655b54dac8SHaiyang Zhang } 11665b54dac8SHaiyang Zhang net_device->num_chn = 1 + 11675b54dac8SHaiyang Zhang init_packet->msg.v5_msg.subchn_comp.num_subchannels; 11685b54dac8SHaiyang Zhang 11695b54dac8SHaiyang Zhang ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn); 11705b54dac8SHaiyang Zhang 1171b3e6b82aSKY Srinivasan /* 1172d66ab514SHaiyang Zhang * Set the number of sub-channels to be received. 1173b3e6b82aSKY Srinivasan */ 1174b3e6b82aSKY Srinivasan spin_lock_irqsave(&net_device->sc_lock, flags); 1175b3e6b82aSKY Srinivasan sc_delta = num_rss_qs - (net_device->num_chn - 1); 1176b3e6b82aSKY Srinivasan net_device->num_sc_offered -= sc_delta; 1177b3e6b82aSKY Srinivasan spin_unlock_irqrestore(&net_device->sc_lock, flags); 1178b3e6b82aSKY Srinivasan 11795b54dac8SHaiyang Zhang out: 118059995370SAndrew Schwartzmeyer if (ret) { 118159995370SAndrew Schwartzmeyer net_device->max_chn = 1; 11825b54dac8SHaiyang Zhang net_device->num_chn = 1; 1183d66ab514SHaiyang Zhang net_device->num_sc_offered = 0; 118459995370SAndrew Schwartzmeyer } 1185b3e6b82aSKY Srinivasan 11865b54dac8SHaiyang Zhang return 0; /* return 0 because primary channel can be used alone */ 11874a0e70aeSKY Srinivasan 11884a0e70aeSKY Srinivasan err_dev_remv: 11894a0e70aeSKY Srinivasan rndis_filter_device_remove(dev); 11904a0e70aeSKY Srinivasan return ret; 119195fa0405SHaiyang Zhang } 119295fa0405SHaiyang Zhang 119395fa0405SHaiyang Zhang void rndis_filter_device_remove(struct hv_device *dev) 119495fa0405SHaiyang Zhang { 11952625466dSVitaly Kuznetsov struct netvsc_device *net_dev = hv_device_to_netvsc_device(dev); 119695fa0405SHaiyang Zhang struct rndis_device *rndis_dev = net_dev->extension; 1197d66ab514SHaiyang Zhang unsigned long t; 1198d66ab514SHaiyang Zhang 1199d66ab514SHaiyang Zhang /* If not all subchannel offers are complete, wait for them until 1200d66ab514SHaiyang Zhang * completion to avoid race. 1201d66ab514SHaiyang Zhang */ 1202d66ab514SHaiyang Zhang while (net_dev->num_sc_offered > 0) { 1203d66ab514SHaiyang Zhang t = wait_for_completion_timeout(&net_dev->channel_init_wait, 1204d66ab514SHaiyang Zhang 10 * HZ); 1205d66ab514SHaiyang Zhang if (t == 0) 1206d66ab514SHaiyang Zhang WARN(1, "Netvsc: Waiting for sub-channel processing"); 1207d66ab514SHaiyang Zhang } 120895fa0405SHaiyang Zhang 120995fa0405SHaiyang Zhang /* Halt and release the rndis device */ 121095fa0405SHaiyang Zhang rndis_filter_halt_device(rndis_dev); 121195fa0405SHaiyang Zhang 121295fa0405SHaiyang Zhang kfree(rndis_dev); 121395fa0405SHaiyang Zhang net_dev->extension = NULL; 121495fa0405SHaiyang Zhang 121595fa0405SHaiyang Zhang netvsc_device_remove(dev); 121695fa0405SHaiyang Zhang } 121795fa0405SHaiyang Zhang 121895fa0405SHaiyang Zhang 121995fa0405SHaiyang Zhang int rndis_filter_open(struct hv_device *dev) 122095fa0405SHaiyang Zhang { 12212625466dSVitaly Kuznetsov struct netvsc_device *net_device = hv_device_to_netvsc_device(dev); 122295fa0405SHaiyang Zhang 122395fa0405SHaiyang Zhang if (!net_device) 122495fa0405SHaiyang Zhang return -EINVAL; 122595fa0405SHaiyang Zhang 122684bf9cefSKY Srinivasan if (atomic_inc_return(&net_device->open_cnt) != 1) 122784bf9cefSKY Srinivasan return 0; 122884bf9cefSKY Srinivasan 122995fa0405SHaiyang Zhang return rndis_filter_open_device(net_device->extension); 123095fa0405SHaiyang Zhang } 123195fa0405SHaiyang Zhang 123295fa0405SHaiyang Zhang int rndis_filter_close(struct hv_device *dev) 123395fa0405SHaiyang Zhang { 12342625466dSVitaly Kuznetsov struct netvsc_device *nvdev = hv_device_to_netvsc_device(dev); 123595fa0405SHaiyang Zhang 12365fccab3bSHaiyang Zhang if (!nvdev) 123795fa0405SHaiyang Zhang return -EINVAL; 123895fa0405SHaiyang Zhang 123984bf9cefSKY Srinivasan if (atomic_dec_return(&nvdev->open_cnt) != 0) 124084bf9cefSKY Srinivasan return 0; 124184bf9cefSKY Srinivasan 12425fccab3bSHaiyang Zhang return rndis_filter_close_device(nvdev->extension); 124395fa0405SHaiyang Zhang } 1244