19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 295fa0405SHaiyang Zhang /* 395fa0405SHaiyang Zhang * Copyright (c) 2009, Microsoft Corporation. 495fa0405SHaiyang Zhang * 595fa0405SHaiyang Zhang * Authors: 695fa0405SHaiyang Zhang * Haiyang Zhang <haiyangz@microsoft.com> 795fa0405SHaiyang Zhang * Hank Janssen <hjanssen@microsoft.com> 895fa0405SHaiyang Zhang */ 9cc69837fSJakub Kicinski #include <linux/ethtool.h> 1095fa0405SHaiyang Zhang #include <linux/kernel.h> 1195fa0405SHaiyang Zhang #include <linux/sched.h> 1295fa0405SHaiyang Zhang #include <linux/wait.h> 1395fa0405SHaiyang Zhang #include <linux/highmem.h> 1495fa0405SHaiyang Zhang #include <linux/slab.h> 1595fa0405SHaiyang Zhang #include <linux/io.h> 1695fa0405SHaiyang Zhang #include <linux/if_ether.h> 1795fa0405SHaiyang Zhang #include <linux/netdevice.h> 181f5f3a75SHaiyang Zhang #include <linux/if_vlan.h> 191ce09e89SHaiyang Zhang #include <linux/nls.h> 20d6472302SStephen Rothwell #include <linux/vmalloc.h> 2127f5aa92Sstephen hemminger #include <linux/rtnetlink.h> 220fe554a4SStephen Hemminger #include <linux/ucs2_string.h> 2395fa0405SHaiyang Zhang 2495fa0405SHaiyang Zhang #include "hyperv_net.h" 25ec966381SStephen Hemminger #include "netvsc_trace.h" 2695fa0405SHaiyang Zhang 274f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w); 2895fa0405SHaiyang Zhang 2911d8620eSBoqun Feng #define RNDIS_EXT_LEN HV_HYP_PAGE_SIZE 3095fa0405SHaiyang Zhang struct rndis_request { 3195fa0405SHaiyang Zhang struct list_head list_ent; 3295fa0405SHaiyang Zhang struct completion wait_event; 3395fa0405SHaiyang Zhang 3495fa0405SHaiyang Zhang struct rndis_message response_msg; 35a3a6cab5SHaiyang Zhang /* 36a3a6cab5SHaiyang Zhang * The buffer for extended info after the RNDIS response message. It's 37a3a6cab5SHaiyang Zhang * referenced based on the data offset in the RNDIS message. Its size 38a3a6cab5SHaiyang Zhang * is enough for current needs, and should be sufficient for the near 39a3a6cab5SHaiyang Zhang * future. 40a3a6cab5SHaiyang Zhang */ 41a3a6cab5SHaiyang Zhang u8 response_ext[RNDIS_EXT_LEN]; 4295fa0405SHaiyang Zhang 4395fa0405SHaiyang Zhang /* Simplify allocation by having a netvsc packet inline */ 4495fa0405SHaiyang Zhang struct hv_netvsc_packet pkt; 450f48917bSHaiyang Zhang 4695fa0405SHaiyang Zhang struct rndis_message request_msg; 470f48917bSHaiyang Zhang /* 48a3a6cab5SHaiyang Zhang * The buffer for the extended info after the RNDIS request message. 49a3a6cab5SHaiyang Zhang * It is referenced and sized in a similar way as response_ext. 500f48917bSHaiyang Zhang */ 51a3a6cab5SHaiyang Zhang u8 request_ext[RNDIS_EXT_LEN]; 5295fa0405SHaiyang Zhang }; 5395fa0405SHaiyang Zhang 54962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = { 55962f3feeSstephen hemminger 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 56962f3feeSstephen hemminger 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 57962f3feeSstephen hemminger 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, 58962f3feeSstephen hemminger 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, 59962f3feeSstephen hemminger 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa 60962f3feeSstephen hemminger }; 61962f3feeSstephen hemminger 6295fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void) 6395fa0405SHaiyang Zhang { 6495fa0405SHaiyang Zhang struct rndis_device *device; 6595fa0405SHaiyang Zhang 6695fa0405SHaiyang Zhang device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL); 6795fa0405SHaiyang Zhang if (!device) 6895fa0405SHaiyang Zhang return NULL; 6995fa0405SHaiyang Zhang 7095fa0405SHaiyang Zhang spin_lock_init(&device->request_lock); 7195fa0405SHaiyang Zhang 7295fa0405SHaiyang Zhang INIT_LIST_HEAD(&device->req_list); 734f19c0d8Sstephen hemminger INIT_WORK(&device->mcast_work, rndis_set_multicast); 7495fa0405SHaiyang Zhang 7595fa0405SHaiyang Zhang device->state = RNDIS_DEV_UNINITIALIZED; 7695fa0405SHaiyang Zhang 7795fa0405SHaiyang Zhang return device; 7895fa0405SHaiyang Zhang } 7995fa0405SHaiyang Zhang 8095fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev, 8195fa0405SHaiyang Zhang u32 msg_type, 8295fa0405SHaiyang Zhang u32 msg_len) 8395fa0405SHaiyang Zhang { 8495fa0405SHaiyang Zhang struct rndis_request *request; 8595fa0405SHaiyang Zhang struct rndis_message *rndis_msg; 8695fa0405SHaiyang Zhang struct rndis_set_request *set; 8795fa0405SHaiyang Zhang unsigned long flags; 8895fa0405SHaiyang Zhang 8995fa0405SHaiyang Zhang request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL); 9095fa0405SHaiyang Zhang if (!request) 9195fa0405SHaiyang Zhang return NULL; 9295fa0405SHaiyang Zhang 9395fa0405SHaiyang Zhang init_completion(&request->wait_event); 9495fa0405SHaiyang Zhang 9595fa0405SHaiyang Zhang rndis_msg = &request->request_msg; 9695fa0405SHaiyang Zhang rndis_msg->ndis_msg_type = msg_type; 9795fa0405SHaiyang Zhang rndis_msg->msg_len = msg_len; 9895fa0405SHaiyang Zhang 995b54dac8SHaiyang Zhang request->pkt.q_idx = 0; 1005b54dac8SHaiyang Zhang 10195fa0405SHaiyang Zhang /* 10295fa0405SHaiyang Zhang * Set the request id. This field is always after the rndis header for 10395fa0405SHaiyang Zhang * request/response packet types so we just used the SetRequest as a 10495fa0405SHaiyang Zhang * template 10595fa0405SHaiyang Zhang */ 10695fa0405SHaiyang Zhang set = &rndis_msg->msg.set_req; 10795fa0405SHaiyang Zhang set->req_id = atomic_inc_return(&dev->new_req_id); 10895fa0405SHaiyang Zhang 10995fa0405SHaiyang Zhang /* Add to the request list */ 11095fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 11195fa0405SHaiyang Zhang list_add_tail(&request->list_ent, &dev->req_list); 11295fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 11395fa0405SHaiyang Zhang 11495fa0405SHaiyang Zhang return request; 11595fa0405SHaiyang Zhang } 11695fa0405SHaiyang Zhang 11795fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev, 11895fa0405SHaiyang Zhang struct rndis_request *req) 11995fa0405SHaiyang Zhang { 12095fa0405SHaiyang Zhang unsigned long flags; 12195fa0405SHaiyang Zhang 12295fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 12395fa0405SHaiyang Zhang list_del(&req->list_ent); 12495fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 12595fa0405SHaiyang Zhang 12695fa0405SHaiyang Zhang kfree(req); 12795fa0405SHaiyang Zhang } 12895fa0405SHaiyang Zhang 12979cf1baeSStephen Hemminger static void dump_rndis_message(struct net_device *netdev, 1300ba35fe9SAndrea Parri (Microsoft) const struct rndis_message *rndis_msg, 1310ba35fe9SAndrea Parri (Microsoft) const void *data) 13295fa0405SHaiyang Zhang { 13395fa0405SHaiyang Zhang switch (rndis_msg->ndis_msg_type) { 13451491167SLinus Walleij case RNDIS_MSG_PACKET: 135505e3f00SAndrea Parri (Microsoft) if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= sizeof(struct rndis_packet)) { 1360ba35fe9SAndrea Parri (Microsoft) const struct rndis_packet *pkt = data + RNDIS_HEADER_SIZE; 13751491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, " 13895fa0405SHaiyang Zhang "data offset %u data len %u, # oob %u, " 13995fa0405SHaiyang Zhang "oob offset %u, oob len %u, pkt offset %u, " 14095fa0405SHaiyang Zhang "pkt len %u\n", 14195fa0405SHaiyang Zhang rndis_msg->msg_len, 142505e3f00SAndrea Parri (Microsoft) pkt->data_offset, 143505e3f00SAndrea Parri (Microsoft) pkt->data_len, 144505e3f00SAndrea Parri (Microsoft) pkt->num_oob_data_elements, 145505e3f00SAndrea Parri (Microsoft) pkt->oob_data_offset, 146505e3f00SAndrea Parri (Microsoft) pkt->oob_data_len, 147505e3f00SAndrea Parri (Microsoft) pkt->per_pkt_info_offset, 148505e3f00SAndrea Parri (Microsoft) pkt->per_pkt_info_len); 149505e3f00SAndrea Parri (Microsoft) } 15095fa0405SHaiyang Zhang break; 15195fa0405SHaiyang Zhang 15251491167SLinus Walleij case RNDIS_MSG_INIT_C: 153505e3f00SAndrea Parri (Microsoft) if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= 154505e3f00SAndrea Parri (Microsoft) sizeof(struct rndis_initialize_complete)) { 155505e3f00SAndrea Parri (Microsoft) const struct rndis_initialize_complete *init_complete = 1560ba35fe9SAndrea Parri (Microsoft) data + RNDIS_HEADER_SIZE; 15751491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_INIT_C " 15895fa0405SHaiyang Zhang "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " 15995fa0405SHaiyang Zhang "device flags %d, max xfer size 0x%x, max pkts %u, " 16095fa0405SHaiyang Zhang "pkt aligned %u)\n", 16195fa0405SHaiyang Zhang rndis_msg->msg_len, 162505e3f00SAndrea Parri (Microsoft) init_complete->req_id, 163505e3f00SAndrea Parri (Microsoft) init_complete->status, 164505e3f00SAndrea Parri (Microsoft) init_complete->major_ver, 165505e3f00SAndrea Parri (Microsoft) init_complete->minor_ver, 166505e3f00SAndrea Parri (Microsoft) init_complete->dev_flags, 167505e3f00SAndrea Parri (Microsoft) init_complete->max_xfer_size, 168505e3f00SAndrea Parri (Microsoft) init_complete->max_pkt_per_msg, 169505e3f00SAndrea Parri (Microsoft) init_complete->pkt_alignment_factor); 170505e3f00SAndrea Parri (Microsoft) } 17195fa0405SHaiyang Zhang break; 17295fa0405SHaiyang Zhang 17351491167SLinus Walleij case RNDIS_MSG_QUERY_C: 174505e3f00SAndrea Parri (Microsoft) if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= 175505e3f00SAndrea Parri (Microsoft) sizeof(struct rndis_query_complete)) { 176505e3f00SAndrea Parri (Microsoft) const struct rndis_query_complete *query_complete = 1770ba35fe9SAndrea Parri (Microsoft) data + RNDIS_HEADER_SIZE; 17851491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_QUERY_C " 17995fa0405SHaiyang Zhang "(len %u, id 0x%x, status 0x%x, buf len %u, " 18095fa0405SHaiyang Zhang "buf offset %u)\n", 18195fa0405SHaiyang Zhang rndis_msg->msg_len, 182505e3f00SAndrea Parri (Microsoft) query_complete->req_id, 183505e3f00SAndrea Parri (Microsoft) query_complete->status, 184505e3f00SAndrea Parri (Microsoft) query_complete->info_buflen, 185505e3f00SAndrea Parri (Microsoft) query_complete->info_buf_offset); 186505e3f00SAndrea Parri (Microsoft) } 18795fa0405SHaiyang Zhang break; 18895fa0405SHaiyang Zhang 18951491167SLinus Walleij case RNDIS_MSG_SET_C: 190505e3f00SAndrea Parri (Microsoft) if (rndis_msg->msg_len - RNDIS_HEADER_SIZE + sizeof(struct rndis_set_complete)) { 191505e3f00SAndrea Parri (Microsoft) const struct rndis_set_complete *set_complete = 1920ba35fe9SAndrea Parri (Microsoft) data + RNDIS_HEADER_SIZE; 19395fa0405SHaiyang Zhang netdev_dbg(netdev, 19451491167SLinus Walleij "RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n", 19595fa0405SHaiyang Zhang rndis_msg->msg_len, 196505e3f00SAndrea Parri (Microsoft) set_complete->req_id, 197505e3f00SAndrea Parri (Microsoft) set_complete->status); 198505e3f00SAndrea Parri (Microsoft) } 19995fa0405SHaiyang Zhang break; 20095fa0405SHaiyang Zhang 20151491167SLinus Walleij case RNDIS_MSG_INDICATE: 202505e3f00SAndrea Parri (Microsoft) if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= 203505e3f00SAndrea Parri (Microsoft) sizeof(struct rndis_indicate_status)) { 204505e3f00SAndrea Parri (Microsoft) const struct rndis_indicate_status *indicate_status = 2050ba35fe9SAndrea Parri (Microsoft) data + RNDIS_HEADER_SIZE; 20651491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_INDICATE " 20795fa0405SHaiyang Zhang "(len %u, status 0x%x, buf len %u, buf offset %u)\n", 20895fa0405SHaiyang Zhang rndis_msg->msg_len, 209505e3f00SAndrea Parri (Microsoft) indicate_status->status, 210505e3f00SAndrea Parri (Microsoft) indicate_status->status_buflen, 211505e3f00SAndrea Parri (Microsoft) indicate_status->status_buf_offset); 212505e3f00SAndrea Parri (Microsoft) } 21395fa0405SHaiyang Zhang break; 21495fa0405SHaiyang Zhang 21595fa0405SHaiyang Zhang default: 21695fa0405SHaiyang Zhang netdev_dbg(netdev, "0x%x (len %u)\n", 21795fa0405SHaiyang Zhang rndis_msg->ndis_msg_type, 21895fa0405SHaiyang Zhang rndis_msg->msg_len); 21995fa0405SHaiyang Zhang break; 22095fa0405SHaiyang Zhang } 22195fa0405SHaiyang Zhang } 22295fa0405SHaiyang Zhang 22395fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev, 22495fa0405SHaiyang Zhang struct rndis_request *req) 22595fa0405SHaiyang Zhang { 22695fa0405SHaiyang Zhang struct hv_netvsc_packet *packet; 227b08cc791SKY Srinivasan struct hv_page_buffer page_buf[2]; 228a9f2e2d6SKY Srinivasan struct hv_page_buffer *pb = page_buf; 22902b6de01Sstephen hemminger int ret; 23095fa0405SHaiyang Zhang 23195fa0405SHaiyang Zhang /* Setup the packet to send it */ 23295fa0405SHaiyang Zhang packet = &req->pkt; 23395fa0405SHaiyang Zhang 23495fa0405SHaiyang Zhang packet->total_data_buflen = req->request_msg.msg_len; 23595fa0405SHaiyang Zhang packet->page_buf_cnt = 1; 23695fa0405SHaiyang Zhang 237a9f2e2d6SKY Srinivasan pb[0].pfn = virt_to_phys(&req->request_msg) >> 23811d8620eSBoqun Feng HV_HYP_PAGE_SHIFT; 239a9f2e2d6SKY Srinivasan pb[0].len = req->request_msg.msg_len; 24011d8620eSBoqun Feng pb[0].offset = offset_in_hvpage(&req->request_msg); 24195fa0405SHaiyang Zhang 24299e3fcfaSHaiyang Zhang /* Add one page_buf when request_msg crossing page boundary */ 24311d8620eSBoqun Feng if (pb[0].offset + pb[0].len > HV_HYP_PAGE_SIZE) { 24499e3fcfaSHaiyang Zhang packet->page_buf_cnt++; 24511d8620eSBoqun Feng pb[0].len = HV_HYP_PAGE_SIZE - 246a9f2e2d6SKY Srinivasan pb[0].offset; 247a9f2e2d6SKY Srinivasan pb[1].pfn = virt_to_phys((void *)&req->request_msg 24811d8620eSBoqun Feng + pb[0].len) >> HV_HYP_PAGE_SHIFT; 249a9f2e2d6SKY Srinivasan pb[1].offset = 0; 250a9f2e2d6SKY Srinivasan pb[1].len = req->request_msg.msg_len - 251a9f2e2d6SKY Srinivasan pb[0].len; 25299e3fcfaSHaiyang Zhang } 25399e3fcfaSHaiyang Zhang 254ec966381SStephen Hemminger trace_rndis_send(dev->ndev, 0, &req->request_msg); 255ec966381SStephen Hemminger 256867047c4Sstephen hemminger rcu_read_lock_bh(); 257351e1581SHaiyang Zhang ret = netvsc_send(dev->ndev, packet, NULL, pb, NULL, false); 258867047c4Sstephen hemminger rcu_read_unlock_bh(); 259867047c4Sstephen hemminger 26095fa0405SHaiyang Zhang return ret; 26195fa0405SHaiyang Zhang } 26295fa0405SHaiyang Zhang 2631b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev, 2641b07da51SHaiyang Zhang struct rndis_request *request) 2651b07da51SHaiyang Zhang { 2661b07da51SHaiyang Zhang u32 link_status; 2671b07da51SHaiyang Zhang struct rndis_query_complete *query_complete; 268505e3f00SAndrea Parri (Microsoft) u32 msg_len = request->response_msg.msg_len; 269505e3f00SAndrea Parri (Microsoft) 270505e3f00SAndrea Parri (Microsoft) /* Ensure the packet is big enough to access its fields */ 271505e3f00SAndrea Parri (Microsoft) if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete)) 272505e3f00SAndrea Parri (Microsoft) return; 2731b07da51SHaiyang Zhang 2741b07da51SHaiyang Zhang query_complete = &request->response_msg.msg.query_complete; 2751b07da51SHaiyang Zhang 2761b07da51SHaiyang Zhang if (query_complete->status == RNDIS_STATUS_SUCCESS && 277505e3f00SAndrea Parri (Microsoft) query_complete->info_buflen >= sizeof(u32) && 278505e3f00SAndrea Parri (Microsoft) query_complete->info_buf_offset >= sizeof(*query_complete) && 279505e3f00SAndrea Parri (Microsoft) msg_len - RNDIS_HEADER_SIZE >= query_complete->info_buf_offset && 280505e3f00SAndrea Parri (Microsoft) msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset 281505e3f00SAndrea Parri (Microsoft) >= query_complete->info_buflen) { 2821b07da51SHaiyang Zhang memcpy(&link_status, (void *)((unsigned long)query_complete + 2831b07da51SHaiyang Zhang query_complete->info_buf_offset), sizeof(u32)); 2841b07da51SHaiyang Zhang rdev->link_state = link_status != 0; 2851b07da51SHaiyang Zhang } 2861b07da51SHaiyang Zhang } 2871b07da51SHaiyang Zhang 28802400fceSStephen Hemminger static void rndis_filter_receive_response(struct net_device *ndev, 28902400fceSStephen Hemminger struct netvsc_device *nvdev, 2900ba35fe9SAndrea Parri (Microsoft) struct rndis_message *resp, 2910ba35fe9SAndrea Parri (Microsoft) void *data) 29295fa0405SHaiyang Zhang { 2930ba35fe9SAndrea Parri (Microsoft) u32 *req_id = &resp->msg.init_complete.req_id; 29402400fceSStephen Hemminger struct rndis_device *dev = nvdev->extension; 29595fa0405SHaiyang Zhang struct rndis_request *request = NULL; 29695fa0405SHaiyang Zhang bool found = false; 29795fa0405SHaiyang Zhang unsigned long flags; 29802400fceSStephen Hemminger 29902400fceSStephen Hemminger /* This should never happen, it means control message 30002400fceSStephen Hemminger * response received after device removed. 30102400fceSStephen Hemminger */ 30202400fceSStephen Hemminger if (dev->state == RNDIS_DEV_UNINITIALIZED) { 30302400fceSStephen Hemminger netdev_err(ndev, 30402400fceSStephen Hemminger "got rndis message uninitialized\n"); 30502400fceSStephen Hemminger return; 30602400fceSStephen Hemminger } 30795fa0405SHaiyang Zhang 30844144185SAndres Beltran /* Ensure the packet is big enough to read req_id. Req_id is the 1st 30944144185SAndres Beltran * field in any request/response message, so the payload should have at 31044144185SAndres Beltran * least sizeof(u32) bytes 31144144185SAndres Beltran */ 31244144185SAndres Beltran if (resp->msg_len - RNDIS_HEADER_SIZE < sizeof(u32)) { 31344144185SAndres Beltran netdev_err(ndev, "rndis msg_len too small: %u\n", 31444144185SAndres Beltran resp->msg_len); 31544144185SAndres Beltran return; 31644144185SAndres Beltran } 31744144185SAndres Beltran 3180ba35fe9SAndrea Parri (Microsoft) /* Copy the request ID into nvchan->recv_buf */ 3190ba35fe9SAndrea Parri (Microsoft) *req_id = *(u32 *)(data + RNDIS_HEADER_SIZE); 3200ba35fe9SAndrea Parri (Microsoft) 32195fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 32295fa0405SHaiyang Zhang list_for_each_entry(request, &dev->req_list, list_ent) { 32395fa0405SHaiyang Zhang /* 32495fa0405SHaiyang Zhang * All request/response message contains RequestId as the 1st 32595fa0405SHaiyang Zhang * field 32695fa0405SHaiyang Zhang */ 3270ba35fe9SAndrea Parri (Microsoft) if (request->request_msg.msg.init_req.req_id == *req_id) { 32895fa0405SHaiyang Zhang found = true; 32995fa0405SHaiyang Zhang break; 33095fa0405SHaiyang Zhang } 33195fa0405SHaiyang Zhang } 33295fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 33395fa0405SHaiyang Zhang 33495fa0405SHaiyang Zhang if (found) { 335a3a6cab5SHaiyang Zhang if (resp->msg_len <= 336a3a6cab5SHaiyang Zhang sizeof(struct rndis_message) + RNDIS_EXT_LEN) { 3370ba35fe9SAndrea Parri (Microsoft) memcpy(&request->response_msg, resp, RNDIS_HEADER_SIZE + sizeof(*req_id)); 3380ba35fe9SAndrea Parri (Microsoft) memcpy((void *)&request->response_msg + RNDIS_HEADER_SIZE + sizeof(*req_id), 3390ba35fe9SAndrea Parri (Microsoft) data + RNDIS_HEADER_SIZE + sizeof(*req_id), 3400ba35fe9SAndrea Parri (Microsoft) resp->msg_len - RNDIS_HEADER_SIZE - sizeof(*req_id)); 3411b07da51SHaiyang Zhang if (request->request_msg.ndis_msg_type == 3421b07da51SHaiyang Zhang RNDIS_MSG_QUERY && request->request_msg.msg. 3431b07da51SHaiyang Zhang query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS) 3441b07da51SHaiyang Zhang rndis_set_link_state(dev, request); 34595fa0405SHaiyang Zhang } else { 34695fa0405SHaiyang Zhang netdev_err(ndev, 34795fa0405SHaiyang Zhang "rndis response buffer overflow " 34895fa0405SHaiyang Zhang "detected (size %u max %zu)\n", 34995fa0405SHaiyang Zhang resp->msg_len, 35086eedaccSKY Srinivasan sizeof(struct rndis_message)); 35195fa0405SHaiyang Zhang 35295fa0405SHaiyang Zhang if (resp->ndis_msg_type == 35351491167SLinus Walleij RNDIS_MSG_RESET_C) { 35495fa0405SHaiyang Zhang /* does not have a request id field */ 35595fa0405SHaiyang Zhang request->response_msg.msg.reset_complete. 356007e5c8eSLinus Walleij status = RNDIS_STATUS_BUFFER_OVERFLOW; 35795fa0405SHaiyang Zhang } else { 35895fa0405SHaiyang Zhang request->response_msg.msg. 35995fa0405SHaiyang Zhang init_complete.status = 360007e5c8eSLinus Walleij RNDIS_STATUS_BUFFER_OVERFLOW; 36195fa0405SHaiyang Zhang } 36295fa0405SHaiyang Zhang } 36395fa0405SHaiyang Zhang 36495fa0405SHaiyang Zhang complete(&request->wait_event); 36595fa0405SHaiyang Zhang } else { 36695fa0405SHaiyang Zhang netdev_err(ndev, 36795fa0405SHaiyang Zhang "no rndis request found for this response " 36895fa0405SHaiyang Zhang "(id 0x%x res type 0x%x)\n", 3690ba35fe9SAndrea Parri (Microsoft) *req_id, 37095fa0405SHaiyang Zhang resp->ndis_msg_type); 37195fa0405SHaiyang Zhang } 37295fa0405SHaiyang Zhang } 37395fa0405SHaiyang Zhang 3741f5f3a75SHaiyang Zhang /* 3751f5f3a75SHaiyang Zhang * Get the Per-Packet-Info with the specified type 3761f5f3a75SHaiyang Zhang * return NULL if not found. 3771f5f3a75SHaiyang Zhang */ 37844144185SAndres Beltran static inline void *rndis_get_ppi(struct net_device *ndev, 37944144185SAndres Beltran struct rndis_packet *rpkt, 380505e3f00SAndrea Parri (Microsoft) u32 rpkt_len, u32 type, u8 internal, 3810ba35fe9SAndrea Parri (Microsoft) u32 ppi_size, void *data) 3821f5f3a75SHaiyang Zhang { 3831f5f3a75SHaiyang Zhang struct rndis_per_packet_info *ppi; 3841f5f3a75SHaiyang Zhang int len; 3851f5f3a75SHaiyang Zhang 3861f5f3a75SHaiyang Zhang if (rpkt->per_pkt_info_offset == 0) 3871f5f3a75SHaiyang Zhang return NULL; 3881f5f3a75SHaiyang Zhang 38944144185SAndres Beltran /* Validate info_offset and info_len */ 39044144185SAndres Beltran if (rpkt->per_pkt_info_offset < sizeof(struct rndis_packet) || 39144144185SAndres Beltran rpkt->per_pkt_info_offset > rpkt_len) { 39244144185SAndres Beltran netdev_err(ndev, "Invalid per_pkt_info_offset: %u\n", 39344144185SAndres Beltran rpkt->per_pkt_info_offset); 39444144185SAndres Beltran return NULL; 39544144185SAndres Beltran } 39644144185SAndres Beltran 397505e3f00SAndrea Parri (Microsoft) if (rpkt->per_pkt_info_len < sizeof(*ppi) || 398505e3f00SAndrea Parri (Microsoft) rpkt->per_pkt_info_len > rpkt_len - rpkt->per_pkt_info_offset) { 39944144185SAndres Beltran netdev_err(ndev, "Invalid per_pkt_info_len: %u\n", 40044144185SAndres Beltran rpkt->per_pkt_info_len); 40144144185SAndres Beltran return NULL; 40244144185SAndres Beltran } 40344144185SAndres Beltran 4041f5f3a75SHaiyang Zhang ppi = (struct rndis_per_packet_info *)((ulong)rpkt + 4051f5f3a75SHaiyang Zhang rpkt->per_pkt_info_offset); 4060ba35fe9SAndrea Parri (Microsoft) /* Copy the PPIs into nvchan->recv_buf */ 4070ba35fe9SAndrea Parri (Microsoft) memcpy(ppi, data + RNDIS_HEADER_SIZE + rpkt->per_pkt_info_offset, rpkt->per_pkt_info_len); 4081f5f3a75SHaiyang Zhang len = rpkt->per_pkt_info_len; 4091f5f3a75SHaiyang Zhang 4101f5f3a75SHaiyang Zhang while (len > 0) { 41144144185SAndres Beltran /* Validate ppi_offset and ppi_size */ 41244144185SAndres Beltran if (ppi->size > len) { 41344144185SAndres Beltran netdev_err(ndev, "Invalid ppi size: %u\n", ppi->size); 41444144185SAndres Beltran continue; 41544144185SAndres Beltran } 41644144185SAndres Beltran 41744144185SAndres Beltran if (ppi->ppi_offset >= ppi->size) { 41844144185SAndres Beltran netdev_err(ndev, "Invalid ppi_offset: %u\n", ppi->ppi_offset); 41944144185SAndres Beltran continue; 42044144185SAndres Beltran } 42144144185SAndres Beltran 422505e3f00SAndrea Parri (Microsoft) if (ppi->type == type && ppi->internal == internal) { 423505e3f00SAndrea Parri (Microsoft) /* ppi->size should be big enough to hold the returned object. */ 424505e3f00SAndrea Parri (Microsoft) if (ppi->size - ppi->ppi_offset < ppi_size) { 425505e3f00SAndrea Parri (Microsoft) netdev_err(ndev, "Invalid ppi: size %u ppi_offset %u\n", 426505e3f00SAndrea Parri (Microsoft) ppi->size, ppi->ppi_offset); 427505e3f00SAndrea Parri (Microsoft) continue; 428505e3f00SAndrea Parri (Microsoft) } 4291f5f3a75SHaiyang Zhang return (void *)((ulong)ppi + ppi->ppi_offset); 430505e3f00SAndrea Parri (Microsoft) } 4311f5f3a75SHaiyang Zhang len -= ppi->size; 4321f5f3a75SHaiyang Zhang ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size); 4331f5f3a75SHaiyang Zhang } 4341f5f3a75SHaiyang Zhang 4351f5f3a75SHaiyang Zhang return NULL; 4361f5f3a75SHaiyang Zhang } 4371f5f3a75SHaiyang Zhang 438c8e4eff4SHaiyang Zhang static inline 439c8e4eff4SHaiyang Zhang void rsc_add_data(struct netvsc_channel *nvchan, 440c8e4eff4SHaiyang Zhang const struct ndis_pkt_8021q_info *vlan, 441c8e4eff4SHaiyang Zhang const struct ndis_tcp_ip_checksum_info *csum_info, 4421fac7ca4SStephen Hemminger const u32 *hash_info, 443c8e4eff4SHaiyang Zhang void *data, u32 len) 444c8e4eff4SHaiyang Zhang { 445c8e4eff4SHaiyang Zhang u32 cnt = nvchan->rsc.cnt; 446c8e4eff4SHaiyang Zhang 447c8e4eff4SHaiyang Zhang if (cnt) { 448c8e4eff4SHaiyang Zhang nvchan->rsc.pktlen += len; 449c8e4eff4SHaiyang Zhang } else { 4500ba35fe9SAndrea Parri (Microsoft) /* The data/values pointed by vlan, csum_info and hash_info are shared 4510ba35fe9SAndrea Parri (Microsoft) * across the different 'fragments' of the RSC packet; store them into 4520ba35fe9SAndrea Parri (Microsoft) * the packet itself. 4530ba35fe9SAndrea Parri (Microsoft) */ 4540ba35fe9SAndrea Parri (Microsoft) if (vlan != NULL) { 4550ba35fe9SAndrea Parri (Microsoft) memcpy(&nvchan->rsc.vlan, vlan, sizeof(*vlan)); 4560ba35fe9SAndrea Parri (Microsoft) nvchan->rsc.ppi_flags |= NVSC_RSC_VLAN; 4570ba35fe9SAndrea Parri (Microsoft) } else { 4580ba35fe9SAndrea Parri (Microsoft) nvchan->rsc.ppi_flags &= ~NVSC_RSC_VLAN; 4590ba35fe9SAndrea Parri (Microsoft) } 4600ba35fe9SAndrea Parri (Microsoft) if (csum_info != NULL) { 4610ba35fe9SAndrea Parri (Microsoft) memcpy(&nvchan->rsc.csum_info, csum_info, sizeof(*csum_info)); 4620ba35fe9SAndrea Parri (Microsoft) nvchan->rsc.ppi_flags |= NVSC_RSC_CSUM_INFO; 4630ba35fe9SAndrea Parri (Microsoft) } else { 4640ba35fe9SAndrea Parri (Microsoft) nvchan->rsc.ppi_flags &= ~NVSC_RSC_CSUM_INFO; 4650ba35fe9SAndrea Parri (Microsoft) } 466c8e4eff4SHaiyang Zhang nvchan->rsc.pktlen = len; 4670ba35fe9SAndrea Parri (Microsoft) if (hash_info != NULL) { 4688dff9808SAndrea Parri (Microsoft) nvchan->rsc.hash_info = *hash_info; 4690ba35fe9SAndrea Parri (Microsoft) nvchan->rsc.ppi_flags |= NVSC_RSC_HASH_INFO; 4700ba35fe9SAndrea Parri (Microsoft) } else { 4710ba35fe9SAndrea Parri (Microsoft) nvchan->rsc.ppi_flags &= ~NVSC_RSC_HASH_INFO; 4720ba35fe9SAndrea Parri (Microsoft) } 473c8e4eff4SHaiyang Zhang } 474c8e4eff4SHaiyang Zhang 475c8e4eff4SHaiyang Zhang nvchan->rsc.data[cnt] = data; 476c8e4eff4SHaiyang Zhang nvchan->rsc.len[cnt] = len; 477c8e4eff4SHaiyang Zhang nvchan->rsc.cnt++; 478c8e4eff4SHaiyang Zhang } 479c8e4eff4SHaiyang Zhang 480dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev, 481345ac089SStephen Hemminger struct netvsc_device *nvdev, 482c8e4eff4SHaiyang Zhang struct netvsc_channel *nvchan, 4833be9b5fdSHaiyang Zhang struct rndis_message *msg, 4840ba35fe9SAndrea Parri (Microsoft) void *data, u32 data_buflen) 48595fa0405SHaiyang Zhang { 486dc54a08cSstephen hemminger struct rndis_packet *rndis_pkt = &msg->msg.pkt; 487dc54a08cSstephen hemminger const struct ndis_tcp_ip_checksum_info *csum_info; 488dc54a08cSstephen hemminger const struct ndis_pkt_8021q_info *vlan; 489c8e4eff4SHaiyang Zhang const struct rndis_pktinfo_id *pktinfo_id; 4901fac7ca4SStephen Hemminger const u32 *hash_info; 49144144185SAndres Beltran u32 data_offset, rpkt_len; 492c8e4eff4SHaiyang Zhang bool rsc_more = false; 493c8e4eff4SHaiyang Zhang int ret; 49495fa0405SHaiyang Zhang 49544144185SAndres Beltran /* Ensure data_buflen is big enough to read header fields */ 49644144185SAndres Beltran if (data_buflen < RNDIS_HEADER_SIZE + sizeof(struct rndis_packet)) { 49744144185SAndres Beltran netdev_err(ndev, "invalid rndis pkt, data_buflen too small: %u\n", 49844144185SAndres Beltran data_buflen); 49944144185SAndres Beltran return NVSP_STAT_FAIL; 50044144185SAndres Beltran } 50144144185SAndres Beltran 5020ba35fe9SAndrea Parri (Microsoft) /* Copy the RNDIS packet into nvchan->recv_buf */ 5030ba35fe9SAndrea Parri (Microsoft) memcpy(rndis_pkt, data + RNDIS_HEADER_SIZE, sizeof(*rndis_pkt)); 5040ba35fe9SAndrea Parri (Microsoft) 50544144185SAndres Beltran /* Validate rndis_pkt offset */ 50644144185SAndres Beltran if (rndis_pkt->data_offset >= data_buflen - RNDIS_HEADER_SIZE) { 50744144185SAndres Beltran netdev_err(ndev, "invalid rndis packet offset: %u\n", 50844144185SAndres Beltran rndis_pkt->data_offset); 50944144185SAndres Beltran return NVSP_STAT_FAIL; 51044144185SAndres Beltran } 51144144185SAndres Beltran 51295fa0405SHaiyang Zhang /* Remove the rndis header and pass it back up the stack */ 51395fa0405SHaiyang Zhang data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; 51495fa0405SHaiyang Zhang 51544144185SAndres Beltran rpkt_len = data_buflen - RNDIS_HEADER_SIZE; 516dc54a08cSstephen hemminger data_buflen -= data_offset; 5174b8a8bc9SWei Yongjun 5184b8a8bc9SWei Yongjun /* 5194b8a8bc9SWei Yongjun * Make sure we got a valid RNDIS message, now total_data_buflen 5204b8a8bc9SWei Yongjun * should be the data packet size plus the trailer padding size 5214b8a8bc9SWei Yongjun */ 522dc54a08cSstephen hemminger if (unlikely(data_buflen < rndis_pkt->data_len)) { 52302400fceSStephen Hemminger netdev_err(ndev, "rndis message buffer " 5244b8a8bc9SWei Yongjun "overflow detected (got %u, min %u)" 5254b8a8bc9SWei Yongjun "...dropping this message!\n", 526dc54a08cSstephen hemminger data_buflen, rndis_pkt->data_len); 52710082f98SKY Srinivasan return NVSP_STAT_FAIL; 5284b8a8bc9SWei Yongjun } 5294b8a8bc9SWei Yongjun 5300ba35fe9SAndrea Parri (Microsoft) vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0, sizeof(*vlan), 5310ba35fe9SAndrea Parri (Microsoft) data); 532dc54a08cSstephen hemminger 533505e3f00SAndrea Parri (Microsoft) csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0, 5340ba35fe9SAndrea Parri (Microsoft) sizeof(*csum_info), data); 535c8e4eff4SHaiyang Zhang 536505e3f00SAndrea Parri (Microsoft) hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0, 5370ba35fe9SAndrea Parri (Microsoft) sizeof(*hash_info), data); 5381fac7ca4SStephen Hemminger 539505e3f00SAndrea Parri (Microsoft) pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1, 5400ba35fe9SAndrea Parri (Microsoft) sizeof(*pktinfo_id), data); 5413be9b5fdSHaiyang Zhang 542c8e4eff4SHaiyang Zhang /* Identify RSC frags, drop erroneous packets */ 543c8e4eff4SHaiyang Zhang if (pktinfo_id && (pktinfo_id->flag & RNDIS_PKTINFO_SUBALLOC)) { 544c8e4eff4SHaiyang Zhang if (pktinfo_id->flag & RNDIS_PKTINFO_1ST_FRAG) 545c8e4eff4SHaiyang Zhang nvchan->rsc.cnt = 0; 546c8e4eff4SHaiyang Zhang else if (nvchan->rsc.cnt == 0) 547c8e4eff4SHaiyang Zhang goto drop; 548c8e4eff4SHaiyang Zhang 549c8e4eff4SHaiyang Zhang rsc_more = true; 550c8e4eff4SHaiyang Zhang 551c8e4eff4SHaiyang Zhang if (pktinfo_id->flag & RNDIS_PKTINFO_LAST_FRAG) 552c8e4eff4SHaiyang Zhang rsc_more = false; 553c8e4eff4SHaiyang Zhang 554c8e4eff4SHaiyang Zhang if (rsc_more && nvchan->rsc.is_last) 555c8e4eff4SHaiyang Zhang goto drop; 556c8e4eff4SHaiyang Zhang } else { 557c8e4eff4SHaiyang Zhang nvchan->rsc.cnt = 0; 558c8e4eff4SHaiyang Zhang } 559c8e4eff4SHaiyang Zhang 560c8e4eff4SHaiyang Zhang if (unlikely(nvchan->rsc.cnt >= NVSP_RSC_MAX)) 561c8e4eff4SHaiyang Zhang goto drop; 562c8e4eff4SHaiyang Zhang 563c8e4eff4SHaiyang Zhang /* Put data into per channel structure. 564c8e4eff4SHaiyang Zhang * Also, remove the rndis trailer padding from rndis packet message 5654b8a8bc9SWei Yongjun * rndis_pkt->data_len tell us the real data length, we only copy 5664b8a8bc9SWei Yongjun * the data packet to the stack, without the rndis trailer padding 5674b8a8bc9SWei Yongjun */ 5681fac7ca4SStephen Hemminger rsc_add_data(nvchan, vlan, csum_info, hash_info, 5690ba35fe9SAndrea Parri (Microsoft) data + data_offset, rndis_pkt->data_len); 570c8e4eff4SHaiyang Zhang 571c8e4eff4SHaiyang Zhang if (rsc_more) 572c8e4eff4SHaiyang Zhang return NVSP_STAT_SUCCESS; 573c8e4eff4SHaiyang Zhang 574c8e4eff4SHaiyang Zhang ret = netvsc_recv_callback(ndev, nvdev, nvchan); 575c8e4eff4SHaiyang Zhang nvchan->rsc.cnt = 0; 576c8e4eff4SHaiyang Zhang 577c8e4eff4SHaiyang Zhang return ret; 578c8e4eff4SHaiyang Zhang 579c8e4eff4SHaiyang Zhang drop: 580c8e4eff4SHaiyang Zhang return NVSP_STAT_FAIL; 58195fa0405SHaiyang Zhang } 58295fa0405SHaiyang Zhang 583dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev, 584dc54a08cSstephen hemminger struct netvsc_device *net_dev, 585c8e4eff4SHaiyang Zhang struct netvsc_channel *nvchan, 586dc54a08cSstephen hemminger void *data, u32 buflen) 58795fa0405SHaiyang Zhang { 5883d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(ndev); 5890ba35fe9SAndrea Parri (Microsoft) struct rndis_message *rndis_msg = nvchan->recv_buf; 5900ba35fe9SAndrea Parri (Microsoft) 5910ba35fe9SAndrea Parri (Microsoft) if (buflen < RNDIS_HEADER_SIZE) { 5920ba35fe9SAndrea Parri (Microsoft) netdev_err(ndev, "Invalid rndis_msg (buflen: %u)\n", buflen); 5930ba35fe9SAndrea Parri (Microsoft) return NVSP_STAT_FAIL; 5940ba35fe9SAndrea Parri (Microsoft) } 5950ba35fe9SAndrea Parri (Microsoft) 5960ba35fe9SAndrea Parri (Microsoft) /* Copy the RNDIS msg header into nvchan->recv_buf */ 5970ba35fe9SAndrea Parri (Microsoft) memcpy(rndis_msg, data, RNDIS_HEADER_SIZE); 59895fa0405SHaiyang Zhang 59944144185SAndres Beltran /* Validate incoming rndis_message packet */ 6000ba35fe9SAndrea Parri (Microsoft) if (rndis_msg->msg_len < RNDIS_HEADER_SIZE || 60144144185SAndres Beltran buflen < rndis_msg->msg_len) { 60244144185SAndres Beltran netdev_err(ndev, "Invalid rndis_msg (buflen: %u, msg_len: %u)\n", 60344144185SAndres Beltran buflen, rndis_msg->msg_len); 60444144185SAndres Beltran return NVSP_STAT_FAIL; 60544144185SAndres Beltran } 60644144185SAndres Beltran 607505e3f00SAndrea Parri (Microsoft) if (netif_msg_rx_status(net_device_ctx)) 6080ba35fe9SAndrea Parri (Microsoft) dump_rndis_message(ndev, rndis_msg, data); 609505e3f00SAndrea Parri (Microsoft) 610ef31bef6SHaiyang Zhang switch (rndis_msg->ndis_msg_type) { 61151491167SLinus Walleij case RNDIS_MSG_PACKET: 612c8e4eff4SHaiyang Zhang return rndis_filter_receive_data(ndev, net_dev, nvchan, 6130ba35fe9SAndrea Parri (Microsoft) rndis_msg, data, buflen); 61451491167SLinus Walleij case RNDIS_MSG_INIT_C: 61551491167SLinus Walleij case RNDIS_MSG_QUERY_C: 61651491167SLinus Walleij case RNDIS_MSG_SET_C: 61795fa0405SHaiyang Zhang /* completion msgs */ 6180ba35fe9SAndrea Parri (Microsoft) rndis_filter_receive_response(ndev, net_dev, rndis_msg, data); 61995fa0405SHaiyang Zhang break; 62095fa0405SHaiyang Zhang 62151491167SLinus Walleij case RNDIS_MSG_INDICATE: 62295fa0405SHaiyang Zhang /* notification msgs */ 623*3946688eSAndrea Parri (Microsoft) netvsc_linkstatus_callback(ndev, rndis_msg, data, buflen); 62495fa0405SHaiyang Zhang break; 62595fa0405SHaiyang Zhang default: 62695fa0405SHaiyang Zhang netdev_err(ndev, 62795fa0405SHaiyang Zhang "unhandled rndis message (type %u len %u)\n", 628ef31bef6SHaiyang Zhang rndis_msg->ndis_msg_type, 629ef31bef6SHaiyang Zhang rndis_msg->msg_len); 6305c71dadbSHaiyang Zhang return NVSP_STAT_FAIL; 63195fa0405SHaiyang Zhang } 63295fa0405SHaiyang Zhang 6335c71dadbSHaiyang Zhang return NVSP_STAT_SUCCESS; 63495fa0405SHaiyang Zhang } 63595fa0405SHaiyang Zhang 636867047c4Sstephen hemminger static int rndis_filter_query_device(struct rndis_device *dev, 637867047c4Sstephen hemminger struct netvsc_device *nvdev, 638867047c4Sstephen hemminger u32 oid, void *result, u32 *result_size) 63995fa0405SHaiyang Zhang { 64095fa0405SHaiyang Zhang struct rndis_request *request; 64195fa0405SHaiyang Zhang u32 inresult_size = *result_size; 64295fa0405SHaiyang Zhang struct rndis_query_request *query; 64395fa0405SHaiyang Zhang struct rndis_query_complete *query_complete; 644505e3f00SAndrea Parri (Microsoft) u32 msg_len; 64595fa0405SHaiyang Zhang int ret = 0; 64695fa0405SHaiyang Zhang 64795fa0405SHaiyang Zhang if (!result) 64895fa0405SHaiyang Zhang return -EINVAL; 64995fa0405SHaiyang Zhang 65095fa0405SHaiyang Zhang *result_size = 0; 65151491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_QUERY, 65295fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_query_request)); 65395fa0405SHaiyang Zhang if (!request) { 65495fa0405SHaiyang Zhang ret = -ENOMEM; 65595fa0405SHaiyang Zhang goto cleanup; 65695fa0405SHaiyang Zhang } 65795fa0405SHaiyang Zhang 65895fa0405SHaiyang Zhang /* Setup the rndis query */ 65995fa0405SHaiyang Zhang query = &request->request_msg.msg.query_req; 66095fa0405SHaiyang Zhang query->oid = oid; 66195fa0405SHaiyang Zhang query->info_buf_offset = sizeof(struct rndis_query_request); 66295fa0405SHaiyang Zhang query->info_buflen = 0; 66395fa0405SHaiyang Zhang query->dev_vc_handle = 0; 66495fa0405SHaiyang Zhang 66523312a3bSstephen hemminger if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) { 66623312a3bSstephen hemminger struct ndis_offload *hwcaps; 66723312a3bSstephen hemminger u32 nvsp_version = nvdev->nvsp_version; 66823312a3bSstephen hemminger u8 ndis_rev; 66923312a3bSstephen hemminger size_t size; 67023312a3bSstephen hemminger 67123312a3bSstephen hemminger if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) { 67223312a3bSstephen hemminger ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3; 67323312a3bSstephen hemminger size = NDIS_OFFLOAD_SIZE; 67423312a3bSstephen hemminger } else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) { 67523312a3bSstephen hemminger ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2; 67623312a3bSstephen hemminger size = NDIS_OFFLOAD_SIZE_6_1; 67723312a3bSstephen hemminger } else { 67823312a3bSstephen hemminger ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1; 67923312a3bSstephen hemminger size = NDIS_OFFLOAD_SIZE_6_0; 68023312a3bSstephen hemminger } 68123312a3bSstephen hemminger 68223312a3bSstephen hemminger request->request_msg.msg_len += size; 68323312a3bSstephen hemminger query->info_buflen = size; 68423312a3bSstephen hemminger hwcaps = (struct ndis_offload *) 68523312a3bSstephen hemminger ((unsigned long)query + query->info_buf_offset); 68623312a3bSstephen hemminger 68723312a3bSstephen hemminger hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD; 68823312a3bSstephen hemminger hwcaps->header.revision = ndis_rev; 68923312a3bSstephen hemminger hwcaps->header.size = size; 69023312a3bSstephen hemminger 69123312a3bSstephen hemminger } else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) { 6925b54dac8SHaiyang Zhang struct ndis_recv_scale_cap *cap; 6935b54dac8SHaiyang Zhang 6945b54dac8SHaiyang Zhang request->request_msg.msg_len += 6955b54dac8SHaiyang Zhang sizeof(struct ndis_recv_scale_cap); 6965b54dac8SHaiyang Zhang query->info_buflen = sizeof(struct ndis_recv_scale_cap); 6975b54dac8SHaiyang Zhang cap = (struct ndis_recv_scale_cap *)((unsigned long)query + 6985b54dac8SHaiyang Zhang query->info_buf_offset); 6995b54dac8SHaiyang Zhang cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES; 7005b54dac8SHaiyang Zhang cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2; 7015b54dac8SHaiyang Zhang cap->hdr.size = sizeof(struct ndis_recv_scale_cap); 7025b54dac8SHaiyang Zhang } 7035b54dac8SHaiyang Zhang 70495fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 70595fa0405SHaiyang Zhang if (ret != 0) 70695fa0405SHaiyang Zhang goto cleanup; 70795fa0405SHaiyang Zhang 7085362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 70995fa0405SHaiyang Zhang 71095fa0405SHaiyang Zhang /* Copy the response back */ 71195fa0405SHaiyang Zhang query_complete = &request->response_msg.msg.query_complete; 712505e3f00SAndrea Parri (Microsoft) msg_len = request->response_msg.msg_len; 71395fa0405SHaiyang Zhang 714505e3f00SAndrea Parri (Microsoft) /* Ensure the packet is big enough to access its fields */ 715505e3f00SAndrea Parri (Microsoft) if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete)) { 716505e3f00SAndrea Parri (Microsoft) ret = -1; 717505e3f00SAndrea Parri (Microsoft) goto cleanup; 718505e3f00SAndrea Parri (Microsoft) } 719505e3f00SAndrea Parri (Microsoft) 720505e3f00SAndrea Parri (Microsoft) if (query_complete->info_buflen > inresult_size || 721505e3f00SAndrea Parri (Microsoft) query_complete->info_buf_offset < sizeof(*query_complete) || 722505e3f00SAndrea Parri (Microsoft) msg_len - RNDIS_HEADER_SIZE < query_complete->info_buf_offset || 723505e3f00SAndrea Parri (Microsoft) msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset 724505e3f00SAndrea Parri (Microsoft) < query_complete->info_buflen) { 72595fa0405SHaiyang Zhang ret = -1; 72695fa0405SHaiyang Zhang goto cleanup; 72795fa0405SHaiyang Zhang } 72895fa0405SHaiyang Zhang 72995fa0405SHaiyang Zhang memcpy(result, 73095fa0405SHaiyang Zhang (void *)((unsigned long)query_complete + 73195fa0405SHaiyang Zhang query_complete->info_buf_offset), 73295fa0405SHaiyang Zhang query_complete->info_buflen); 73395fa0405SHaiyang Zhang 73495fa0405SHaiyang Zhang *result_size = query_complete->info_buflen; 73595fa0405SHaiyang Zhang 73695fa0405SHaiyang Zhang cleanup: 73795fa0405SHaiyang Zhang if (request) 73895fa0405SHaiyang Zhang put_rndis_request(dev, request); 73995fa0405SHaiyang Zhang 74095fa0405SHaiyang Zhang return ret; 74195fa0405SHaiyang Zhang } 74295fa0405SHaiyang Zhang 74323312a3bSstephen hemminger /* Get the hardware offload capabilities */ 74423312a3bSstephen hemminger static int 745867047c4Sstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device, 746867047c4Sstephen hemminger struct ndis_offload *caps) 74723312a3bSstephen hemminger { 74823312a3bSstephen hemminger u32 caps_len = sizeof(*caps); 74923312a3bSstephen hemminger int ret; 75023312a3bSstephen hemminger 75123312a3bSstephen hemminger memset(caps, 0, sizeof(*caps)); 75223312a3bSstephen hemminger 753867047c4Sstephen hemminger ret = rndis_filter_query_device(dev, net_device, 75423312a3bSstephen hemminger OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES, 75523312a3bSstephen hemminger caps, &caps_len); 75623312a3bSstephen hemminger if (ret) 75723312a3bSstephen hemminger return ret; 75823312a3bSstephen hemminger 75923312a3bSstephen hemminger if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) { 76023312a3bSstephen hemminger netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n", 76123312a3bSstephen hemminger caps->header.type); 76223312a3bSstephen hemminger return -EINVAL; 76323312a3bSstephen hemminger } 76423312a3bSstephen hemminger 76523312a3bSstephen hemminger if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) { 76623312a3bSstephen hemminger netdev_warn(dev->ndev, "invalid NDIS objrev %x\n", 76723312a3bSstephen hemminger caps->header.revision); 76823312a3bSstephen hemminger return -EINVAL; 76923312a3bSstephen hemminger } 77023312a3bSstephen hemminger 77123312a3bSstephen hemminger if (caps->header.size > caps_len || 77223312a3bSstephen hemminger caps->header.size < NDIS_OFFLOAD_SIZE_6_0) { 77323312a3bSstephen hemminger netdev_warn(dev->ndev, 77423312a3bSstephen hemminger "invalid NDIS objsize %u, data size %u\n", 77523312a3bSstephen hemminger caps->header.size, caps_len); 77623312a3bSstephen hemminger return -EINVAL; 77723312a3bSstephen hemminger } 77823312a3bSstephen hemminger 77923312a3bSstephen hemminger return 0; 78023312a3bSstephen hemminger } 78123312a3bSstephen hemminger 782867047c4Sstephen hemminger static int rndis_filter_query_device_mac(struct rndis_device *dev, 783867047c4Sstephen hemminger struct netvsc_device *net_device) 78495fa0405SHaiyang Zhang { 78595fa0405SHaiyang Zhang u32 size = ETH_ALEN; 78695fa0405SHaiyang Zhang 787867047c4Sstephen hemminger return rndis_filter_query_device(dev, net_device, 78895fa0405SHaiyang Zhang RNDIS_OID_802_3_PERMANENT_ADDRESS, 78995fa0405SHaiyang Zhang dev->hw_mac_adr, &size); 79095fa0405SHaiyang Zhang } 79195fa0405SHaiyang Zhang 7921ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress" 7931ce09e89SHaiyang Zhang #define NWADR_STRLEN 14 7941ce09e89SHaiyang Zhang 795867047c4Sstephen hemminger int rndis_filter_set_device_mac(struct netvsc_device *nvdev, 796867047c4Sstephen hemminger const char *mac) 7971ce09e89SHaiyang Zhang { 7981ce09e89SHaiyang Zhang struct rndis_device *rdev = nvdev->extension; 7991ce09e89SHaiyang Zhang struct rndis_request *request; 8001ce09e89SHaiyang Zhang struct rndis_set_request *set; 8011ce09e89SHaiyang Zhang struct rndis_config_parameter_info *cpi; 8021ce09e89SHaiyang Zhang wchar_t *cfg_nwadr, *cfg_mac; 8031ce09e89SHaiyang Zhang struct rndis_set_complete *set_complete; 8041ce09e89SHaiyang Zhang char macstr[2*ETH_ALEN+1]; 8051ce09e89SHaiyang Zhang u32 extlen = sizeof(struct rndis_config_parameter_info) + 8061ce09e89SHaiyang Zhang 2*NWADR_STRLEN + 4*ETH_ALEN; 807999028ccSNicholas Mc Guire int ret; 8081ce09e89SHaiyang Zhang 8091ce09e89SHaiyang Zhang request = get_rndis_request(rdev, RNDIS_MSG_SET, 8101ce09e89SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 8111ce09e89SHaiyang Zhang if (!request) 8121ce09e89SHaiyang Zhang return -ENOMEM; 8131ce09e89SHaiyang Zhang 8141ce09e89SHaiyang Zhang set = &request->request_msg.msg.set_req; 8151ce09e89SHaiyang Zhang set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER; 8161ce09e89SHaiyang Zhang set->info_buflen = extlen; 8171ce09e89SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 8181ce09e89SHaiyang Zhang set->dev_vc_handle = 0; 8191ce09e89SHaiyang Zhang 8201ce09e89SHaiyang Zhang cpi = (struct rndis_config_parameter_info *)((ulong)set + 8211ce09e89SHaiyang Zhang set->info_buf_offset); 8221ce09e89SHaiyang Zhang cpi->parameter_name_offset = 8231ce09e89SHaiyang Zhang sizeof(struct rndis_config_parameter_info); 8241ce09e89SHaiyang Zhang /* Multiply by 2 because host needs 2 bytes (utf16) for each char */ 8251ce09e89SHaiyang Zhang cpi->parameter_name_length = 2*NWADR_STRLEN; 8261ce09e89SHaiyang Zhang cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING; 8271ce09e89SHaiyang Zhang cpi->parameter_value_offset = 8281ce09e89SHaiyang Zhang cpi->parameter_name_offset + cpi->parameter_name_length; 8291ce09e89SHaiyang Zhang /* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */ 8301ce09e89SHaiyang Zhang cpi->parameter_value_length = 4*ETH_ALEN; 8311ce09e89SHaiyang Zhang 8321ce09e89SHaiyang Zhang cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset); 8331ce09e89SHaiyang Zhang cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset); 8341ce09e89SHaiyang Zhang ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN, 8351ce09e89SHaiyang Zhang cfg_nwadr, NWADR_STRLEN); 8361ce09e89SHaiyang Zhang if (ret < 0) 8371ce09e89SHaiyang Zhang goto cleanup; 8381ce09e89SHaiyang Zhang snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac); 8391ce09e89SHaiyang Zhang ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN, 8401ce09e89SHaiyang Zhang cfg_mac, 2*ETH_ALEN); 8411ce09e89SHaiyang Zhang if (ret < 0) 8421ce09e89SHaiyang Zhang goto cleanup; 8431ce09e89SHaiyang Zhang 8441ce09e89SHaiyang Zhang ret = rndis_filter_send_request(rdev, request); 8451ce09e89SHaiyang Zhang if (ret != 0) 8461ce09e89SHaiyang Zhang goto cleanup; 8471ce09e89SHaiyang Zhang 8485362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 8495362855aSVitaly Kuznetsov 8501ce09e89SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 851867047c4Sstephen hemminger if (set_complete->status != RNDIS_STATUS_SUCCESS) 852867047c4Sstephen hemminger ret = -EIO; 8531ce09e89SHaiyang Zhang 8541ce09e89SHaiyang Zhang cleanup: 8551ce09e89SHaiyang Zhang put_rndis_request(rdev, request); 8561ce09e89SHaiyang Zhang return ret; 8571ce09e89SHaiyang Zhang } 8581ce09e89SHaiyang Zhang 859d6792a5aSHaiyang Zhang int 860426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev, 8619749fed5Sstephen hemminger struct netvsc_device *nvdev, 8624a0e70aeSKY Srinivasan struct ndis_offload_params *req_offloads) 8634a0e70aeSKY Srinivasan { 8644a0e70aeSKY Srinivasan struct rndis_device *rdev = nvdev->extension; 8654a0e70aeSKY Srinivasan struct rndis_request *request; 8664a0e70aeSKY Srinivasan struct rndis_set_request *set; 8674a0e70aeSKY Srinivasan struct ndis_offload_params *offload_params; 8684a0e70aeSKY Srinivasan struct rndis_set_complete *set_complete; 8694a0e70aeSKY Srinivasan u32 extlen = sizeof(struct ndis_offload_params); 870999028ccSNicholas Mc Guire int ret; 871af9893a3SKY Srinivasan u32 vsp_version = nvdev->nvsp_version; 872af9893a3SKY Srinivasan 873af9893a3SKY Srinivasan if (vsp_version <= NVSP_PROTOCOL_VERSION_4) { 874af9893a3SKY Srinivasan extlen = VERSION_4_OFFLOAD_SIZE; 875af9893a3SKY Srinivasan /* On NVSP_PROTOCOL_VERSION_4 and below, we do not support 876af9893a3SKY Srinivasan * UDP checksum offload. 877af9893a3SKY Srinivasan */ 878af9893a3SKY Srinivasan req_offloads->udp_ip_v4_csum = 0; 879af9893a3SKY Srinivasan req_offloads->udp_ip_v6_csum = 0; 880af9893a3SKY Srinivasan } 8814a0e70aeSKY Srinivasan 8824a0e70aeSKY Srinivasan request = get_rndis_request(rdev, RNDIS_MSG_SET, 8834a0e70aeSKY Srinivasan RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 8844a0e70aeSKY Srinivasan if (!request) 8854a0e70aeSKY Srinivasan return -ENOMEM; 8864a0e70aeSKY Srinivasan 8874a0e70aeSKY Srinivasan set = &request->request_msg.msg.set_req; 8884a0e70aeSKY Srinivasan set->oid = OID_TCP_OFFLOAD_PARAMETERS; 8894a0e70aeSKY Srinivasan set->info_buflen = extlen; 8904a0e70aeSKY Srinivasan set->info_buf_offset = sizeof(struct rndis_set_request); 8914a0e70aeSKY Srinivasan set->dev_vc_handle = 0; 8924a0e70aeSKY Srinivasan 8934a0e70aeSKY Srinivasan offload_params = (struct ndis_offload_params *)((ulong)set + 8944a0e70aeSKY Srinivasan set->info_buf_offset); 8954a0e70aeSKY Srinivasan *offload_params = *req_offloads; 8964a0e70aeSKY Srinivasan offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT; 8974a0e70aeSKY Srinivasan offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3; 8984a0e70aeSKY Srinivasan offload_params->header.size = extlen; 8994a0e70aeSKY Srinivasan 9004a0e70aeSKY Srinivasan ret = rndis_filter_send_request(rdev, request); 9014a0e70aeSKY Srinivasan if (ret != 0) 9024a0e70aeSKY Srinivasan goto cleanup; 9034a0e70aeSKY Srinivasan 9045362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 9054a0e70aeSKY Srinivasan set_complete = &request->response_msg.msg.set_complete; 9064a0e70aeSKY Srinivasan if (set_complete->status != RNDIS_STATUS_SUCCESS) { 907af9893a3SKY Srinivasan netdev_err(ndev, "Fail to set offload on host side:0x%x\n", 9084a0e70aeSKY Srinivasan set_complete->status); 9094a0e70aeSKY Srinivasan ret = -EINVAL; 9104a0e70aeSKY Srinivasan } 9114a0e70aeSKY Srinivasan 9124a0e70aeSKY Srinivasan cleanup: 9134a0e70aeSKY Srinivasan put_rndis_request(rdev, request); 9144a0e70aeSKY Srinivasan return ret; 9154a0e70aeSKY Srinivasan } 9161ce09e89SHaiyang Zhang 917b4a10c75SHaiyang Zhang static int rndis_set_rss_param_msg(struct rndis_device *rdev, 918b4a10c75SHaiyang Zhang const u8 *rss_key, u16 flag) 9195b54dac8SHaiyang Zhang { 9203d541ac5SVitaly Kuznetsov struct net_device *ndev = rdev->ndev; 921b0689faaSHaiyang Zhang struct net_device_context *ndc = netdev_priv(ndev); 9225b54dac8SHaiyang Zhang struct rndis_request *request; 9235b54dac8SHaiyang Zhang struct rndis_set_request *set; 9245b54dac8SHaiyang Zhang struct rndis_set_complete *set_complete; 9255b54dac8SHaiyang Zhang u32 extlen = sizeof(struct ndis_recv_scale_param) + 926962f3feeSstephen hemminger 4 * ITAB_NUM + NETVSC_HASH_KEYLEN; 9275b54dac8SHaiyang Zhang struct ndis_recv_scale_param *rssp; 9285b54dac8SHaiyang Zhang u32 *itab; 9295b54dac8SHaiyang Zhang u8 *keyp; 930999028ccSNicholas Mc Guire int i, ret; 9315b54dac8SHaiyang Zhang 9325b54dac8SHaiyang Zhang request = get_rndis_request( 9335b54dac8SHaiyang Zhang rdev, RNDIS_MSG_SET, 9345b54dac8SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 9355b54dac8SHaiyang Zhang if (!request) 9365b54dac8SHaiyang Zhang return -ENOMEM; 9375b54dac8SHaiyang Zhang 9385b54dac8SHaiyang Zhang set = &request->request_msg.msg.set_req; 9395b54dac8SHaiyang Zhang set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS; 9405b54dac8SHaiyang Zhang set->info_buflen = extlen; 9415b54dac8SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 9425b54dac8SHaiyang Zhang set->dev_vc_handle = 0; 9435b54dac8SHaiyang Zhang 9445b54dac8SHaiyang Zhang rssp = (struct ndis_recv_scale_param *)(set + 1); 9455b54dac8SHaiyang Zhang rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS; 9465b54dac8SHaiyang Zhang rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2; 9475b54dac8SHaiyang Zhang rssp->hdr.size = sizeof(struct ndis_recv_scale_param); 948b4a10c75SHaiyang Zhang rssp->flag = flag; 9495b54dac8SHaiyang Zhang rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 | 9504c87454aSHaiyang Zhang NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 | 9514c87454aSHaiyang Zhang NDIS_HASH_TCP_IPV6; 9525b54dac8SHaiyang Zhang rssp->indirect_tabsize = 4*ITAB_NUM; 9535b54dac8SHaiyang Zhang rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param); 954962f3feeSstephen hemminger rssp->hashkey_size = NETVSC_HASH_KEYLEN; 955bfcbcb67SStephen Hemminger rssp->hashkey_offset = rssp->indirect_taboffset + 9565b54dac8SHaiyang Zhang rssp->indirect_tabsize; 9575b54dac8SHaiyang Zhang 9585b54dac8SHaiyang Zhang /* Set indirection table entries */ 9595b54dac8SHaiyang Zhang itab = (u32 *)(rssp + 1); 9605b54dac8SHaiyang Zhang for (i = 0; i < ITAB_NUM; i++) 961b0689faaSHaiyang Zhang itab[i] = ndc->rx_table[i]; 9625b54dac8SHaiyang Zhang 9635b54dac8SHaiyang Zhang /* Set hask key values */ 964bfcbcb67SStephen Hemminger keyp = (u8 *)((unsigned long)rssp + rssp->hashkey_offset); 965962f3feeSstephen hemminger memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN); 9665b54dac8SHaiyang Zhang 9675b54dac8SHaiyang Zhang ret = rndis_filter_send_request(rdev, request); 9685b54dac8SHaiyang Zhang if (ret != 0) 9695b54dac8SHaiyang Zhang goto cleanup; 9705b54dac8SHaiyang Zhang 9715362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 9725b54dac8SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 973b4a10c75SHaiyang Zhang if (set_complete->status == RNDIS_STATUS_SUCCESS) { 974b4a10c75SHaiyang Zhang if (!(flag & NDIS_RSS_PARAM_FLAG_DISABLE_RSS) && 975b4a10c75SHaiyang Zhang !(flag & NDIS_RSS_PARAM_FLAG_HASH_KEY_UNCHANGED)) 976962f3feeSstephen hemminger memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN); 977b4a10c75SHaiyang Zhang 978b4a10c75SHaiyang Zhang } else { 9795b54dac8SHaiyang Zhang netdev_err(ndev, "Fail to set RSS parameters:0x%x\n", 9805b54dac8SHaiyang Zhang set_complete->status); 9815b54dac8SHaiyang Zhang ret = -EINVAL; 9825b54dac8SHaiyang Zhang } 9835b54dac8SHaiyang Zhang 9845b54dac8SHaiyang Zhang cleanup: 9855b54dac8SHaiyang Zhang put_rndis_request(rdev, request); 9865b54dac8SHaiyang Zhang return ret; 9875b54dac8SHaiyang Zhang } 9885b54dac8SHaiyang Zhang 989b4a10c75SHaiyang Zhang int rndis_filter_set_rss_param(struct rndis_device *rdev, 990b4a10c75SHaiyang Zhang const u8 *rss_key) 991b4a10c75SHaiyang Zhang { 992b4a10c75SHaiyang Zhang /* Disable RSS before change */ 993b4a10c75SHaiyang Zhang rndis_set_rss_param_msg(rdev, rss_key, 994b4a10c75SHaiyang Zhang NDIS_RSS_PARAM_FLAG_DISABLE_RSS); 995b4a10c75SHaiyang Zhang 996b4a10c75SHaiyang Zhang return rndis_set_rss_param_msg(rdev, rss_key, 0); 997b4a10c75SHaiyang Zhang } 998b4a10c75SHaiyang Zhang 999867047c4Sstephen hemminger static int rndis_filter_query_device_link_status(struct rndis_device *dev, 1000867047c4Sstephen hemminger struct netvsc_device *net_device) 100195fa0405SHaiyang Zhang { 100295fa0405SHaiyang Zhang u32 size = sizeof(u32); 100395fa0405SHaiyang Zhang u32 link_status; 100495fa0405SHaiyang Zhang 1005867047c4Sstephen hemminger return rndis_filter_query_device(dev, net_device, 100695fa0405SHaiyang Zhang RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, 100795fa0405SHaiyang Zhang &link_status, &size); 100895fa0405SHaiyang Zhang } 100995fa0405SHaiyang Zhang 1010867047c4Sstephen hemminger static int rndis_filter_query_link_speed(struct rndis_device *dev, 1011867047c4Sstephen hemminger struct netvsc_device *net_device) 1012b37879e6SHaiyang Zhang { 1013b37879e6SHaiyang Zhang u32 size = sizeof(u32); 1014b37879e6SHaiyang Zhang u32 link_speed; 1015b37879e6SHaiyang Zhang struct net_device_context *ndc; 1016b37879e6SHaiyang Zhang int ret; 1017b37879e6SHaiyang Zhang 1018867047c4Sstephen hemminger ret = rndis_filter_query_device(dev, net_device, 1019867047c4Sstephen hemminger RNDIS_OID_GEN_LINK_SPEED, 1020b37879e6SHaiyang Zhang &link_speed, &size); 1021b37879e6SHaiyang Zhang 1022b37879e6SHaiyang Zhang if (!ret) { 1023b37879e6SHaiyang Zhang ndc = netdev_priv(dev->ndev); 1024b37879e6SHaiyang Zhang 1025b37879e6SHaiyang Zhang /* The link speed reported from host is in 100bps unit, so 1026b37879e6SHaiyang Zhang * we convert it to Mbps here. 1027b37879e6SHaiyang Zhang */ 1028b37879e6SHaiyang Zhang ndc->speed = link_speed / 10000; 1029b37879e6SHaiyang Zhang } 1030b37879e6SHaiyang Zhang 1031b37879e6SHaiyang Zhang return ret; 1032b37879e6SHaiyang Zhang } 1033b37879e6SHaiyang Zhang 10344f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev, 10354f19c0d8Sstephen hemminger u32 new_filter) 103695fa0405SHaiyang Zhang { 103795fa0405SHaiyang Zhang struct rndis_request *request; 103895fa0405SHaiyang Zhang struct rndis_set_request *set; 1039999028ccSNicholas Mc Guire int ret; 104095fa0405SHaiyang Zhang 10417eeb4a6eSStephen Hemminger if (dev->filter == new_filter) 10427eeb4a6eSStephen Hemminger return 0; 10437eeb4a6eSStephen Hemminger 104451491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_SET, 104595fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + 104695fa0405SHaiyang Zhang sizeof(u32)); 1047ce12b810Sstephen hemminger if (!request) 1048ce12b810Sstephen hemminger return -ENOMEM; 1049ce12b810Sstephen hemminger 105095fa0405SHaiyang Zhang /* Setup the rndis set */ 105195fa0405SHaiyang Zhang set = &request->request_msg.msg.set_req; 105295fa0405SHaiyang Zhang set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; 105395fa0405SHaiyang Zhang set->info_buflen = sizeof(u32); 105495fa0405SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 105595fa0405SHaiyang Zhang 105695fa0405SHaiyang Zhang memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), 105795fa0405SHaiyang Zhang &new_filter, sizeof(u32)); 105895fa0405SHaiyang Zhang 105995fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 10607eeb4a6eSStephen Hemminger if (ret == 0) { 10615362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 10627eeb4a6eSStephen Hemminger dev->filter = new_filter; 10637eeb4a6eSStephen Hemminger } 106495fa0405SHaiyang Zhang 106595fa0405SHaiyang Zhang put_rndis_request(dev, request); 1066ce12b810Sstephen hemminger 106795fa0405SHaiyang Zhang return ret; 106895fa0405SHaiyang Zhang } 106995fa0405SHaiyang Zhang 10704f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w) 10714f19c0d8Sstephen hemminger { 10724f19c0d8Sstephen hemminger struct rndis_device *rdev 10734f19c0d8Sstephen hemminger = container_of(w, struct rndis_device, mcast_work); 1074009f766cSStephen Hemminger u32 filter = NDIS_PACKET_TYPE_DIRECTED; 1075009f766cSStephen Hemminger unsigned int flags = rdev->ndev->flags; 10764f19c0d8Sstephen hemminger 1077009f766cSStephen Hemminger if (flags & IFF_PROMISC) { 1078009f766cSStephen Hemminger filter = NDIS_PACKET_TYPE_PROMISCUOUS; 1079009f766cSStephen Hemminger } else { 1080f03dbb06SStephen Hemminger if (!netdev_mc_empty(rdev->ndev) || (flags & IFF_ALLMULTI)) 1081de3d50aaSStephen Hemminger filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; 1082009f766cSStephen Hemminger if (flags & IFF_BROADCAST) 1083de3d50aaSStephen Hemminger filter |= NDIS_PACKET_TYPE_BROADCAST; 1084009f766cSStephen Hemminger } 1085009f766cSStephen Hemminger 1086009f766cSStephen Hemminger rndis_filter_set_packet_filter(rdev, filter); 10874f19c0d8Sstephen hemminger } 10884f19c0d8Sstephen hemminger 10894f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev) 10904f19c0d8Sstephen hemminger { 10914f19c0d8Sstephen hemminger struct rndis_device *rdev = nvdev->extension; 10924f19c0d8Sstephen hemminger 10934f19c0d8Sstephen hemminger schedule_work(&rdev->mcast_work); 10944f19c0d8Sstephen hemminger } 10954f19c0d8Sstephen hemminger 1096867047c4Sstephen hemminger static int rndis_filter_init_device(struct rndis_device *dev, 1097867047c4Sstephen hemminger struct netvsc_device *nvdev) 109895fa0405SHaiyang Zhang { 109995fa0405SHaiyang Zhang struct rndis_request *request; 110095fa0405SHaiyang Zhang struct rndis_initialize_request *init; 110195fa0405SHaiyang Zhang struct rndis_initialize_complete *init_complete; 110295fa0405SHaiyang Zhang u32 status; 1103999028ccSNicholas Mc Guire int ret; 110495fa0405SHaiyang Zhang 110551491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_INIT, 110695fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); 110795fa0405SHaiyang Zhang if (!request) { 110895fa0405SHaiyang Zhang ret = -ENOMEM; 110995fa0405SHaiyang Zhang goto cleanup; 111095fa0405SHaiyang Zhang } 111195fa0405SHaiyang Zhang 111295fa0405SHaiyang Zhang /* Setup the rndis set */ 111395fa0405SHaiyang Zhang init = &request->request_msg.msg.init_req; 111495fa0405SHaiyang Zhang init->major_ver = RNDIS_MAJOR_VERSION; 111595fa0405SHaiyang Zhang init->minor_ver = RNDIS_MINOR_VERSION; 1116fb1d074eSHaiyang Zhang init->max_xfer_size = 0x4000; 111795fa0405SHaiyang Zhang 111895fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZING; 111995fa0405SHaiyang Zhang 112095fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 112195fa0405SHaiyang Zhang if (ret != 0) { 112295fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 112395fa0405SHaiyang Zhang goto cleanup; 112495fa0405SHaiyang Zhang } 112595fa0405SHaiyang Zhang 11265362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 112795fa0405SHaiyang Zhang 112895fa0405SHaiyang Zhang init_complete = &request->response_msg.msg.init_complete; 112995fa0405SHaiyang Zhang status = init_complete->status; 113095fa0405SHaiyang Zhang if (status == RNDIS_STATUS_SUCCESS) { 113195fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZED; 11327c3877f2SHaiyang Zhang nvdev->max_pkt = init_complete->max_pkt_per_msg; 11337c3877f2SHaiyang Zhang nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor; 113495fa0405SHaiyang Zhang ret = 0; 113595fa0405SHaiyang Zhang } else { 113695fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 113795fa0405SHaiyang Zhang ret = -EINVAL; 113895fa0405SHaiyang Zhang } 113995fa0405SHaiyang Zhang 114095fa0405SHaiyang Zhang cleanup: 114195fa0405SHaiyang Zhang if (request) 114295fa0405SHaiyang Zhang put_rndis_request(dev, request); 114395fa0405SHaiyang Zhang 114495fa0405SHaiyang Zhang return ret; 114595fa0405SHaiyang Zhang } 114695fa0405SHaiyang Zhang 114746b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev) 114846b4f7f5Sstephen hemminger { 114946b4f7f5Sstephen hemminger int i; 115046b4f7f5Sstephen hemminger 115146b4f7f5Sstephen hemminger for (i = 0; i < nvdev->num_chn; i++) { 115246b4f7f5Sstephen hemminger const struct netvsc_channel *nvchan = &nvdev->chan_table[i]; 115346b4f7f5Sstephen hemminger 11547426b1a5Sstephen hemminger if (nvchan->mrc.first != nvchan->mrc.next) 11557426b1a5Sstephen hemminger return false; 11567426b1a5Sstephen hemminger 115746b4f7f5Sstephen hemminger if (atomic_read(&nvchan->queue_sends) > 0) 115846b4f7f5Sstephen hemminger return false; 115946b4f7f5Sstephen hemminger } 116046b4f7f5Sstephen hemminger 116146b4f7f5Sstephen hemminger return true; 116246b4f7f5Sstephen hemminger } 116346b4f7f5Sstephen hemminger 11640e96460eSStephen Hemminger static void rndis_filter_halt_device(struct netvsc_device *nvdev, 11650e96460eSStephen Hemminger struct rndis_device *dev) 116695fa0405SHaiyang Zhang { 116795fa0405SHaiyang Zhang struct rndis_request *request; 116895fa0405SHaiyang Zhang struct rndis_halt_request *halt; 116995fa0405SHaiyang Zhang 117095fa0405SHaiyang Zhang /* Attempt to do a rndis device halt */ 117151491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_HALT, 117295fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); 117395fa0405SHaiyang Zhang if (!request) 117495fa0405SHaiyang Zhang goto cleanup; 117595fa0405SHaiyang Zhang 117695fa0405SHaiyang Zhang /* Setup the rndis set */ 117795fa0405SHaiyang Zhang halt = &request->request_msg.msg.halt_req; 117895fa0405SHaiyang Zhang halt->req_id = atomic_inc_return(&dev->new_req_id); 117995fa0405SHaiyang Zhang 118095fa0405SHaiyang Zhang /* Ignore return since this msg is optional. */ 118195fa0405SHaiyang Zhang rndis_filter_send_request(dev, request); 118295fa0405SHaiyang Zhang 118395fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 118495fa0405SHaiyang Zhang 118595fa0405SHaiyang Zhang cleanup: 1186ae9e63bbSHaiyang Zhang nvdev->destroy = true; 118700ecfb3bSstephen hemminger 118800ecfb3bSstephen hemminger /* Force flag to be ordered before waiting */ 118900ecfb3bSstephen hemminger wmb(); 1190ae9e63bbSHaiyang Zhang 1191ae9e63bbSHaiyang Zhang /* Wait for all send completions */ 119246b4f7f5Sstephen hemminger wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev)); 1193ae9e63bbSHaiyang Zhang 119495fa0405SHaiyang Zhang if (request) 119595fa0405SHaiyang Zhang put_rndis_request(dev, request); 119695fa0405SHaiyang Zhang } 119795fa0405SHaiyang Zhang 119895fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev) 119995fa0405SHaiyang Zhang { 120095fa0405SHaiyang Zhang int ret; 120195fa0405SHaiyang Zhang 120295fa0405SHaiyang Zhang if (dev->state != RNDIS_DEV_INITIALIZED) 120395fa0405SHaiyang Zhang return 0; 120495fa0405SHaiyang Zhang 120595fa0405SHaiyang Zhang ret = rndis_filter_set_packet_filter(dev, 120695fa0405SHaiyang Zhang NDIS_PACKET_TYPE_BROADCAST | 120795fa0405SHaiyang Zhang NDIS_PACKET_TYPE_ALL_MULTICAST | 120895fa0405SHaiyang Zhang NDIS_PACKET_TYPE_DIRECTED); 120995fa0405SHaiyang Zhang if (ret == 0) 121095fa0405SHaiyang Zhang dev->state = RNDIS_DEV_DATAINITIALIZED; 121195fa0405SHaiyang Zhang 121295fa0405SHaiyang Zhang return ret; 121395fa0405SHaiyang Zhang } 121495fa0405SHaiyang Zhang 121595fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev) 121695fa0405SHaiyang Zhang { 121795fa0405SHaiyang Zhang int ret; 121895fa0405SHaiyang Zhang 121995fa0405SHaiyang Zhang if (dev->state != RNDIS_DEV_DATAINITIALIZED) 122095fa0405SHaiyang Zhang return 0; 122195fa0405SHaiyang Zhang 12224f19c0d8Sstephen hemminger /* Make sure rndis_set_multicast doesn't re-enable filter! */ 12234f19c0d8Sstephen hemminger cancel_work_sync(&dev->mcast_work); 12244f19c0d8Sstephen hemminger 122595fa0405SHaiyang Zhang ret = rndis_filter_set_packet_filter(dev, 0); 1226c3582a2cSHaiyang Zhang if (ret == -ENODEV) 1227c3582a2cSHaiyang Zhang ret = 0; 1228c3582a2cSHaiyang Zhang 122995fa0405SHaiyang Zhang if (ret == 0) 123095fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZED; 123195fa0405SHaiyang Zhang 123295fa0405SHaiyang Zhang return ret; 123395fa0405SHaiyang Zhang } 123495fa0405SHaiyang Zhang 12355b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc) 12365b54dac8SHaiyang Zhang { 12373d541ac5SVitaly Kuznetsov struct net_device *ndev = 12383d541ac5SVitaly Kuznetsov hv_get_drvdata(new_sc->primary_channel->device_obj); 1239867047c4Sstephen hemminger struct net_device_context *ndev_ctx = netdev_priv(ndev); 1240867047c4Sstephen hemminger struct netvsc_device *nvscdev; 12415b54dac8SHaiyang Zhang u16 chn_index = new_sc->offermsg.offer.sub_channel_index; 12426de38af6Sstephen hemminger struct netvsc_channel *nvchan; 12436de38af6Sstephen hemminger int ret; 12445b54dac8SHaiyang Zhang 1245867047c4Sstephen hemminger /* This is safe because this callback only happens when 1246867047c4Sstephen hemminger * new device is being setup and waiting on the channel_init_wait. 1247867047c4Sstephen hemminger */ 1248867047c4Sstephen hemminger nvscdev = rcu_dereference_raw(ndev_ctx->nvdev); 1249867047c4Sstephen hemminger if (!nvscdev || chn_index >= nvscdev->num_chn) 12505b54dac8SHaiyang Zhang return; 12515b54dac8SHaiyang Zhang 12526de38af6Sstephen hemminger nvchan = nvscdev->chan_table + chn_index; 12536de38af6Sstephen hemminger 1254b1dd90ceSK. Y. Srinivasan /* Because the device uses NAPI, all the interrupt batching and 1255b1dd90ceSK. Y. Srinivasan * control is done via Net softirq, not the channel handling 1256b1dd90ceSK. Y. Srinivasan */ 1257b1dd90ceSK. Y. Srinivasan set_channel_read_mode(new_sc, HV_CALL_ISR); 1258b1dd90ceSK. Y. Srinivasan 1259bffb1842SK. Y. Srinivasan /* Set the channel before opening.*/ 1260bffb1842SK. Y. Srinivasan nvchan->channel = new_sc; 1261bffb1842SK. Y. Srinivasan 12624d18fcc9SAndres Beltran new_sc->rqstor_size = netvsc_rqstor_size(netvsc_ring_bytes); 1263a7f99d0fSStephen Hemminger ret = vmbus_open(new_sc, netvsc_ring_bytes, 1264a7f99d0fSStephen Hemminger netvsc_ring_bytes, NULL, 0, 12656de38af6Sstephen hemminger netvsc_channel_cb, nvchan); 126676bb5db5Sstephen hemminger if (ret == 0) 12676de38af6Sstephen hemminger napi_enable(&nvchan->napi); 126876bb5db5Sstephen hemminger else 12698195b139SStephen Hemminger netdev_notice(ndev, "sub channel open failed: %d\n", ret); 127015a863bfSstephen hemminger 12718f2bb1deSStephen Hemminger if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn) 1272732e4985Sstephen hemminger wake_up(&nvscdev->subchan_open); 12735b54dac8SHaiyang Zhang } 12745b54dac8SHaiyang Zhang 12758195b139SStephen Hemminger /* Open sub-channels after completing the handling of the device probe. 12768195b139SStephen Hemminger * This breaks overlap of processing the host message for the 12778195b139SStephen Hemminger * new primary channel with the initialization of sub-channels. 12788195b139SStephen Hemminger */ 127917d91256SHaiyang Zhang int rndis_set_subchannel(struct net_device *ndev, 128017d91256SHaiyang Zhang struct netvsc_device *nvdev, 128117d91256SHaiyang Zhang struct netvsc_device_info *dev_info) 12828195b139SStephen Hemminger { 12838195b139SStephen Hemminger struct nvsp_message *init_packet = &nvdev->channel_init_pkt; 12843ffe64f1SStephen Hemminger struct net_device_context *ndev_ctx = netdev_priv(ndev); 12853ffe64f1SStephen Hemminger struct hv_device *hv_dev = ndev_ctx->device_ctx; 12863ffe64f1SStephen Hemminger struct rndis_device *rdev = nvdev->extension; 12878195b139SStephen Hemminger int i, ret; 12888195b139SStephen Hemminger 12893ffe64f1SStephen Hemminger ASSERT_RTNL(); 12908195b139SStephen Hemminger 12918195b139SStephen Hemminger memset(init_packet, 0, sizeof(struct nvsp_message)); 12928195b139SStephen Hemminger init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL; 12938195b139SStephen Hemminger init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE; 12948195b139SStephen Hemminger init_packet->msg.v5_msg.subchn_req.num_subchannels = 12958195b139SStephen Hemminger nvdev->num_chn - 1; 1296ec966381SStephen Hemminger trace_nvsp_send(ndev, init_packet); 1297ec966381SStephen Hemminger 12988195b139SStephen Hemminger ret = vmbus_sendpacket(hv_dev->channel, init_packet, 12998195b139SStephen Hemminger sizeof(struct nvsp_message), 13008195b139SStephen Hemminger (unsigned long)init_packet, 13018195b139SStephen Hemminger VM_PKT_DATA_INBAND, 13028195b139SStephen Hemminger VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 13038195b139SStephen Hemminger if (ret) { 13048195b139SStephen Hemminger netdev_err(ndev, "sub channel allocate send failed: %d\n", ret); 13053ffe64f1SStephen Hemminger return ret; 13068195b139SStephen Hemminger } 13078195b139SStephen Hemminger 13088195b139SStephen Hemminger wait_for_completion(&nvdev->channel_init_wait); 13098195b139SStephen Hemminger if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) { 13108195b139SStephen Hemminger netdev_err(ndev, "sub channel request failed\n"); 13113ffe64f1SStephen Hemminger return -EIO; 13128195b139SStephen Hemminger } 13138195b139SStephen Hemminger 1314206ad34dSAndrea Parri (Microsoft) /* Check that number of allocated sub channel is within the expected range */ 1315206ad34dSAndrea Parri (Microsoft) if (init_packet->msg.v5_msg.subchn_comp.num_subchannels > nvdev->num_chn - 1) { 1316206ad34dSAndrea Parri (Microsoft) netdev_err(ndev, "invalid number of allocated sub channel\n"); 1317206ad34dSAndrea Parri (Microsoft) return -EINVAL; 1318206ad34dSAndrea Parri (Microsoft) } 13198195b139SStephen Hemminger nvdev->num_chn = 1 + 13208195b139SStephen Hemminger init_packet->msg.v5_msg.subchn_comp.num_subchannels; 13218195b139SStephen Hemminger 13228195b139SStephen Hemminger /* wait for all sub channels to open */ 13238195b139SStephen Hemminger wait_event(nvdev->subchan_open, 13248195b139SStephen Hemminger atomic_read(&nvdev->open_chn) == nvdev->num_chn); 13258195b139SStephen Hemminger 1326c39ea5cbSHaiyang Zhang for (i = 0; i < VRSS_SEND_TAB_SIZE; i++) 1327c39ea5cbSHaiyang Zhang ndev_ctx->tx_table[i] = i % nvdev->num_chn; 1328c39ea5cbSHaiyang Zhang 132952d3b494SAdrian Vladu /* ignore failures from setting rss parameters, still have channels */ 133017d91256SHaiyang Zhang if (dev_info) 133117d91256SHaiyang Zhang rndis_filter_set_rss_param(rdev, dev_info->rss_key); 133217d91256SHaiyang Zhang else 13338195b139SStephen Hemminger rndis_filter_set_rss_param(rdev, netvsc_hash_key); 13348195b139SStephen Hemminger 13358195b139SStephen Hemminger netif_set_real_num_tx_queues(ndev, nvdev->num_chn); 13368195b139SStephen Hemminger netif_set_real_num_rx_queues(ndev, nvdev->num_chn); 13378195b139SStephen Hemminger 13383ffe64f1SStephen Hemminger return 0; 13398195b139SStephen Hemminger } 13408195b139SStephen Hemminger 1341aefd80e8SVitaly Kuznetsov static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device, 1342aefd80e8SVitaly Kuznetsov struct netvsc_device *nvdev) 134395fa0405SHaiyang Zhang { 1344aefd80e8SVitaly Kuznetsov struct net_device *net = rndis_device->ndev; 13453d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(net); 134623312a3bSstephen hemminger struct ndis_offload hwcaps; 13474a0e70aeSKY Srinivasan struct ndis_offload_params offloads; 134823312a3bSstephen hemminger unsigned int gso_max_size = GSO_MAX_SIZE; 1349aefd80e8SVitaly Kuznetsov int ret; 135095fa0405SHaiyang Zhang 135123312a3bSstephen hemminger /* Find HW offload capabilities */ 1352aefd80e8SVitaly Kuznetsov ret = rndis_query_hwcaps(rndis_device, nvdev, &hwcaps); 13539749fed5Sstephen hemminger if (ret != 0) 1354aefd80e8SVitaly Kuznetsov return ret; 135523312a3bSstephen hemminger 135623312a3bSstephen hemminger /* A value of zero means "no change"; now turn on what we want. */ 13574a0e70aeSKY Srinivasan memset(&offloads, 0, sizeof(struct ndis_offload_params)); 135823312a3bSstephen hemminger 135923312a3bSstephen hemminger /* Linux does not care about IP checksum, always does in kernel */ 136023312a3bSstephen hemminger offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED; 136123312a3bSstephen hemminger 1362aefd80e8SVitaly Kuznetsov /* Reset previously set hw_features flags */ 1363aefd80e8SVitaly Kuznetsov net->hw_features &= ~NETVSC_SUPPORTED_HW_FEATURES; 1364aefd80e8SVitaly Kuznetsov net_device_ctx->tx_checksum_mask = 0; 1365aefd80e8SVitaly Kuznetsov 136623312a3bSstephen hemminger /* Compute tx offload settings based on hw capabilities */ 1367aefd80e8SVitaly Kuznetsov net->hw_features |= NETIF_F_RXCSUM; 1368b441f795SHaiyang Zhang net->hw_features |= NETIF_F_SG; 13696f3aeb1bSStephen Hemminger net->hw_features |= NETIF_F_RXHASH; 137023312a3bSstephen hemminger 137123312a3bSstephen hemminger if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) { 137223312a3bSstephen hemminger /* Can checksum TCP */ 137323312a3bSstephen hemminger net->hw_features |= NETIF_F_IP_CSUM; 137423312a3bSstephen hemminger net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP; 137523312a3bSstephen hemminger 13764a0e70aeSKY Srinivasan offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 137723312a3bSstephen hemminger 137823312a3bSstephen hemminger if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) { 13794a0e70aeSKY Srinivasan offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; 138023312a3bSstephen hemminger net->hw_features |= NETIF_F_TSO; 138123312a3bSstephen hemminger 138223312a3bSstephen hemminger if (hwcaps.lsov2.ip4_maxsz < gso_max_size) 138323312a3bSstephen hemminger gso_max_size = hwcaps.lsov2.ip4_maxsz; 138423312a3bSstephen hemminger } 138523312a3bSstephen hemminger 138623312a3bSstephen hemminger if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) { 138723312a3bSstephen hemminger offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 138823312a3bSstephen hemminger net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP; 138923312a3bSstephen hemminger } 139023312a3bSstephen hemminger } 139123312a3bSstephen hemminger 139223312a3bSstephen hemminger if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) { 139323312a3bSstephen hemminger net->hw_features |= NETIF_F_IPV6_CSUM; 139423312a3bSstephen hemminger 139523312a3bSstephen hemminger offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 139623312a3bSstephen hemminger net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP; 139723312a3bSstephen hemminger 139823312a3bSstephen hemminger if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) && 139923312a3bSstephen hemminger (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) { 140023312a3bSstephen hemminger offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; 140123312a3bSstephen hemminger net->hw_features |= NETIF_F_TSO6; 140223312a3bSstephen hemminger 140323312a3bSstephen hemminger if (hwcaps.lsov2.ip6_maxsz < gso_max_size) 140423312a3bSstephen hemminger gso_max_size = hwcaps.lsov2.ip6_maxsz; 140523312a3bSstephen hemminger } 140623312a3bSstephen hemminger 140723312a3bSstephen hemminger if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) { 140823312a3bSstephen hemminger offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 140923312a3bSstephen hemminger net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP; 141023312a3bSstephen hemminger } 141123312a3bSstephen hemminger } 141223312a3bSstephen hemminger 1413c8e4eff4SHaiyang Zhang if (hwcaps.rsc.ip4 && hwcaps.rsc.ip6) { 1414c8e4eff4SHaiyang Zhang net->hw_features |= NETIF_F_LRO; 1415c8e4eff4SHaiyang Zhang 1416d6792a5aSHaiyang Zhang if (net->features & NETIF_F_LRO) { 1417c8e4eff4SHaiyang Zhang offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED; 1418c8e4eff4SHaiyang Zhang offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED; 1419d6792a5aSHaiyang Zhang } else { 1420d6792a5aSHaiyang Zhang offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED; 1421d6792a5aSHaiyang Zhang offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED; 1422d6792a5aSHaiyang Zhang } 1423c8e4eff4SHaiyang Zhang } 1424c8e4eff4SHaiyang Zhang 1425aefd80e8SVitaly Kuznetsov /* In case some hw_features disappeared we need to remove them from 1426aefd80e8SVitaly Kuznetsov * net->features list as they're no longer supported. 1427aefd80e8SVitaly Kuznetsov */ 1428aefd80e8SVitaly Kuznetsov net->features &= ~NETVSC_SUPPORTED_HW_FEATURES | net->hw_features; 1429aefd80e8SVitaly Kuznetsov 143023312a3bSstephen hemminger netif_set_gso_max_size(net, gso_max_size); 14314a0e70aeSKY Srinivasan 1432aefd80e8SVitaly Kuznetsov ret = rndis_filter_set_offload_params(net, nvdev, &offloads); 1433aefd80e8SVitaly Kuznetsov 1434aefd80e8SVitaly Kuznetsov return ret; 1435aefd80e8SVitaly Kuznetsov } 1436aefd80e8SVitaly Kuznetsov 14370fe554a4SStephen Hemminger static void rndis_get_friendly_name(struct net_device *net, 14380fe554a4SStephen Hemminger struct rndis_device *rndis_device, 14390fe554a4SStephen Hemminger struct netvsc_device *net_device) 14400fe554a4SStephen Hemminger { 14410fe554a4SStephen Hemminger ucs2_char_t wname[256]; 14420fe554a4SStephen Hemminger unsigned long len; 14430fe554a4SStephen Hemminger u8 ifalias[256]; 14440fe554a4SStephen Hemminger u32 size; 14450fe554a4SStephen Hemminger 14460fe554a4SStephen Hemminger size = sizeof(wname); 14470fe554a4SStephen Hemminger if (rndis_filter_query_device(rndis_device, net_device, 14480fe554a4SStephen Hemminger RNDIS_OID_GEN_FRIENDLY_NAME, 14490fe554a4SStephen Hemminger wname, &size) != 0) 1450d97cde6aSStephen Hemminger return; /* ignore if host does not support */ 1451d97cde6aSStephen Hemminger 1452d97cde6aSStephen Hemminger if (size == 0) 1453d97cde6aSStephen Hemminger return; /* name not set */ 14540fe554a4SStephen Hemminger 14550fe554a4SStephen Hemminger /* Convert Windows Unicode string to UTF-8 */ 14560fe554a4SStephen Hemminger len = ucs2_as_utf8(ifalias, wname, sizeof(ifalias)); 14570fe554a4SStephen Hemminger 14580fe554a4SStephen Hemminger /* ignore the default value from host */ 14590fe554a4SStephen Hemminger if (strcmp(ifalias, "Network Adapter") != 0) 14600fe554a4SStephen Hemminger dev_set_alias(net, ifalias, len); 14610fe554a4SStephen Hemminger } 14620fe554a4SStephen Hemminger 1463aefd80e8SVitaly Kuznetsov struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, 1464aefd80e8SVitaly Kuznetsov struct netvsc_device_info *device_info) 1465aefd80e8SVitaly Kuznetsov { 1466aefd80e8SVitaly Kuznetsov struct net_device *net = hv_get_drvdata(dev); 1467b0689faaSHaiyang Zhang struct net_device_context *ndc = netdev_priv(net); 1468aefd80e8SVitaly Kuznetsov struct netvsc_device *net_device; 1469aefd80e8SVitaly Kuznetsov struct rndis_device *rndis_device; 1470aefd80e8SVitaly Kuznetsov struct ndis_recv_scale_cap rsscap; 1471aefd80e8SVitaly Kuznetsov u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); 1472aefd80e8SVitaly Kuznetsov u32 mtu, size; 1473aefd80e8SVitaly Kuznetsov u32 num_possible_rss_qs; 1474aefd80e8SVitaly Kuznetsov int i, ret; 1475aefd80e8SVitaly Kuznetsov 1476aefd80e8SVitaly Kuznetsov rndis_device = get_rndis_device(); 1477aefd80e8SVitaly Kuznetsov if (!rndis_device) 1478aefd80e8SVitaly Kuznetsov return ERR_PTR(-ENODEV); 1479aefd80e8SVitaly Kuznetsov 1480aefd80e8SVitaly Kuznetsov /* Let the inner driver handle this first to create the netvsc channel 1481aefd80e8SVitaly Kuznetsov * NOTE! Once the channel is created, we may get a receive callback 1482aefd80e8SVitaly Kuznetsov * (RndisFilterOnReceive()) before this call is completed 1483aefd80e8SVitaly Kuznetsov */ 1484aefd80e8SVitaly Kuznetsov net_device = netvsc_device_add(dev, device_info); 1485aefd80e8SVitaly Kuznetsov if (IS_ERR(net_device)) { 1486aefd80e8SVitaly Kuznetsov kfree(rndis_device); 1487aefd80e8SVitaly Kuznetsov return net_device; 1488aefd80e8SVitaly Kuznetsov } 1489aefd80e8SVitaly Kuznetsov 1490aefd80e8SVitaly Kuznetsov /* Initialize the rndis device */ 1491aefd80e8SVitaly Kuznetsov net_device->max_chn = 1; 1492aefd80e8SVitaly Kuznetsov net_device->num_chn = 1; 1493aefd80e8SVitaly Kuznetsov 1494aefd80e8SVitaly Kuznetsov net_device->extension = rndis_device; 1495aefd80e8SVitaly Kuznetsov rndis_device->ndev = net; 1496aefd80e8SVitaly Kuznetsov 1497aefd80e8SVitaly Kuznetsov /* Send the rndis initialization message */ 1498aefd80e8SVitaly Kuznetsov ret = rndis_filter_init_device(rndis_device, net_device); 1499aefd80e8SVitaly Kuznetsov if (ret != 0) 1500aefd80e8SVitaly Kuznetsov goto err_dev_remv; 1501aefd80e8SVitaly Kuznetsov 1502aefd80e8SVitaly Kuznetsov /* Get the MTU from the host */ 1503aefd80e8SVitaly Kuznetsov size = sizeof(u32); 1504aefd80e8SVitaly Kuznetsov ret = rndis_filter_query_device(rndis_device, net_device, 1505aefd80e8SVitaly Kuznetsov RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, 1506aefd80e8SVitaly Kuznetsov &mtu, &size); 1507aefd80e8SVitaly Kuznetsov if (ret == 0 && size == sizeof(u32) && mtu < net->mtu) 1508aefd80e8SVitaly Kuznetsov net->mtu = mtu; 1509aefd80e8SVitaly Kuznetsov 1510aefd80e8SVitaly Kuznetsov /* Get the mac address */ 1511aefd80e8SVitaly Kuznetsov ret = rndis_filter_query_device_mac(rndis_device, net_device); 1512aefd80e8SVitaly Kuznetsov if (ret != 0) 1513aefd80e8SVitaly Kuznetsov goto err_dev_remv; 1514aefd80e8SVitaly Kuznetsov 1515aefd80e8SVitaly Kuznetsov memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); 1516aefd80e8SVitaly Kuznetsov 15170fe554a4SStephen Hemminger /* Get friendly name as ifalias*/ 15180fe554a4SStephen Hemminger if (!net->ifalias) 15190fe554a4SStephen Hemminger rndis_get_friendly_name(net, rndis_device, net_device); 15200fe554a4SStephen Hemminger 1521aefd80e8SVitaly Kuznetsov /* Query and set hardware capabilities */ 1522aefd80e8SVitaly Kuznetsov ret = rndis_netdev_set_hwcaps(rndis_device, net_device); 1523aefd80e8SVitaly Kuznetsov if (ret != 0) 15244a0e70aeSKY Srinivasan goto err_dev_remv; 15254a0e70aeSKY Srinivasan 1526867047c4Sstephen hemminger rndis_filter_query_device_link_status(rndis_device, net_device); 152795fa0405SHaiyang Zhang 152893ba2222SVitaly Kuznetsov netdev_dbg(net, "Device MAC %pM link state %s\n", 152995fa0405SHaiyang Zhang rndis_device->hw_mac_adr, 1530dedb459eSHaiyang Zhang rndis_device->link_state ? "down" : "up"); 153195fa0405SHaiyang Zhang 15325b54dac8SHaiyang Zhang if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5) 153355be9f25SMohammed Gamal goto out; 15345b54dac8SHaiyang Zhang 1535867047c4Sstephen hemminger rndis_filter_query_link_speed(rndis_device, net_device); 1536b37879e6SHaiyang Zhang 15375b54dac8SHaiyang Zhang /* vRSS setup */ 15385b54dac8SHaiyang Zhang memset(&rsscap, 0, rsscap_size); 1539867047c4Sstephen hemminger ret = rndis_filter_query_device(rndis_device, net_device, 15405b54dac8SHaiyang Zhang OID_GEN_RECEIVE_SCALE_CAPABILITIES, 15415b54dac8SHaiyang Zhang &rsscap, &rsscap_size); 15425b54dac8SHaiyang Zhang if (ret || rsscap.num_recv_que < 2) 15435b54dac8SHaiyang Zhang goto out; 15445b54dac8SHaiyang Zhang 154525a39f7fSHaiyang Zhang /* This guarantees that num_possible_rss_qs <= num_online_cpus */ 154625a39f7fSHaiyang Zhang num_possible_rss_qs = min_t(u32, num_online_cpus(), 15473071ada4Sstephen hemminger rsscap.num_recv_que); 15483071ada4Sstephen hemminger 15493071ada4Sstephen hemminger net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs); 15508ebdcc52SAndrew Schwartzmeyer 15518ebdcc52SAndrew Schwartzmeyer /* We will use the given number of channels if available. */ 15523071ada4Sstephen hemminger net_device->num_chn = min(net_device->max_chn, device_info->num_chn); 1553ff4a4419Sstephen hemminger 1554b0689faaSHaiyang Zhang if (!netif_is_rxfh_configured(net)) { 1555ff4a4419Sstephen hemminger for (i = 0; i < ITAB_NUM; i++) 1556b0689faaSHaiyang Zhang ndc->rx_table[i] = ethtool_rxfh_indir_default( 155747371300SHaiyang Zhang i, net_device->num_chn); 1558b0689faaSHaiyang Zhang } 1559ff4a4419Sstephen hemminger 1560732e4985Sstephen hemminger atomic_set(&net_device->open_chn, 1); 15618195b139SStephen Hemminger vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); 15625b54dac8SHaiyang Zhang 15637426b1a5Sstephen hemminger for (i = 1; i < net_device->num_chn; i++) { 15647426b1a5Sstephen hemminger ret = netvsc_alloc_recv_comp_ring(net_device, i); 15657426b1a5Sstephen hemminger if (ret) { 15667426b1a5Sstephen hemminger while (--i != 0) 15677426b1a5Sstephen hemminger vfree(net_device->chan_table[i].mrc.slots); 15687426b1a5Sstephen hemminger goto out; 15697426b1a5Sstephen hemminger } 15707426b1a5Sstephen hemminger } 15717426b1a5Sstephen hemminger 15728195b139SStephen Hemminger for (i = 1; i < net_device->num_chn; i++) 15738195b139SStephen Hemminger netif_napi_add(net, &net_device->chan_table[i].napi, 15748195b139SStephen Hemminger netvsc_poll, NAPI_POLL_WEIGHT); 15755b54dac8SHaiyang Zhang 15763ffe64f1SStephen Hemminger return net_device; 15775362855aSVitaly Kuznetsov 15785b54dac8SHaiyang Zhang out: 15793ffe64f1SStephen Hemminger /* setting up multiple channels failed */ 158059995370SAndrew Schwartzmeyer net_device->max_chn = 1; 15815b54dac8SHaiyang Zhang net_device->num_chn = 1; 1582b19b4634STakashi Iwai return net_device; 15834a0e70aeSKY Srinivasan 15844a0e70aeSKY Srinivasan err_dev_remv: 15852289f0aaSstephen hemminger rndis_filter_device_remove(dev, net_device); 15869749fed5Sstephen hemminger return ERR_PTR(ret); 158795fa0405SHaiyang Zhang } 158895fa0405SHaiyang Zhang 15892289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev, 15902289f0aaSstephen hemminger struct netvsc_device *net_dev) 159195fa0405SHaiyang Zhang { 159295fa0405SHaiyang Zhang struct rndis_device *rndis_dev = net_dev->extension; 1593d66ab514SHaiyang Zhang 159495fa0405SHaiyang Zhang /* Halt and release the rndis device */ 15950e96460eSStephen Hemminger rndis_filter_halt_device(net_dev, rndis_dev); 159695fa0405SHaiyang Zhang 159795fa0405SHaiyang Zhang netvsc_device_remove(dev); 159895fa0405SHaiyang Zhang } 159995fa0405SHaiyang Zhang 16002f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev) 160195fa0405SHaiyang Zhang { 16022f5fa6c8SVitaly Kuznetsov if (!nvdev) 160395fa0405SHaiyang Zhang return -EINVAL; 160495fa0405SHaiyang Zhang 16052f5fa6c8SVitaly Kuznetsov return rndis_filter_open_device(nvdev->extension); 160695fa0405SHaiyang Zhang } 160795fa0405SHaiyang Zhang 16082f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev) 160995fa0405SHaiyang Zhang { 16105fccab3bSHaiyang Zhang if (!nvdev) 161195fa0405SHaiyang Zhang return -EINVAL; 161295fa0405SHaiyang Zhang 16135fccab3bSHaiyang Zhang return rndis_filter_close_device(nvdev->extension); 161495fa0405SHaiyang Zhang } 1615