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> 3127f5aa92Sstephen hemminger #include <linux/rtnetlink.h> 3295fa0405SHaiyang Zhang 3395fa0405SHaiyang Zhang #include "hyperv_net.h" 3495fa0405SHaiyang Zhang 354f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w); 3695fa0405SHaiyang Zhang 375b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE 3895fa0405SHaiyang Zhang struct rndis_request { 3995fa0405SHaiyang Zhang struct list_head list_ent; 4095fa0405SHaiyang Zhang struct completion wait_event; 4195fa0405SHaiyang Zhang 4295fa0405SHaiyang Zhang struct rndis_message response_msg; 43a3a6cab5SHaiyang Zhang /* 44a3a6cab5SHaiyang Zhang * The buffer for extended info after the RNDIS response message. It's 45a3a6cab5SHaiyang Zhang * referenced based on the data offset in the RNDIS message. Its size 46a3a6cab5SHaiyang Zhang * is enough for current needs, and should be sufficient for the near 47a3a6cab5SHaiyang Zhang * future. 48a3a6cab5SHaiyang Zhang */ 49a3a6cab5SHaiyang Zhang u8 response_ext[RNDIS_EXT_LEN]; 5095fa0405SHaiyang Zhang 5195fa0405SHaiyang Zhang /* Simplify allocation by having a netvsc packet inline */ 5295fa0405SHaiyang Zhang struct hv_netvsc_packet pkt; 530f48917bSHaiyang Zhang 5495fa0405SHaiyang Zhang struct rndis_message request_msg; 550f48917bSHaiyang Zhang /* 56a3a6cab5SHaiyang Zhang * The buffer for the extended info after the RNDIS request message. 57a3a6cab5SHaiyang Zhang * It is referenced and sized in a similar way as response_ext. 580f48917bSHaiyang Zhang */ 59a3a6cab5SHaiyang Zhang u8 request_ext[RNDIS_EXT_LEN]; 6095fa0405SHaiyang Zhang }; 6195fa0405SHaiyang Zhang 62962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = { 63962f3feeSstephen hemminger 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 64962f3feeSstephen hemminger 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 65962f3feeSstephen hemminger 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, 66962f3feeSstephen hemminger 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, 67962f3feeSstephen hemminger 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa 68962f3feeSstephen hemminger }; 69962f3feeSstephen hemminger 7095fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void) 7195fa0405SHaiyang Zhang { 7295fa0405SHaiyang Zhang struct rndis_device *device; 7395fa0405SHaiyang Zhang 7495fa0405SHaiyang Zhang device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL); 7595fa0405SHaiyang Zhang if (!device) 7695fa0405SHaiyang Zhang return NULL; 7795fa0405SHaiyang Zhang 7895fa0405SHaiyang Zhang spin_lock_init(&device->request_lock); 7995fa0405SHaiyang Zhang 8095fa0405SHaiyang Zhang INIT_LIST_HEAD(&device->req_list); 814f19c0d8Sstephen hemminger INIT_WORK(&device->mcast_work, rndis_set_multicast); 8295fa0405SHaiyang Zhang 8395fa0405SHaiyang Zhang device->state = RNDIS_DEV_UNINITIALIZED; 8495fa0405SHaiyang Zhang 8595fa0405SHaiyang Zhang return device; 8695fa0405SHaiyang Zhang } 8795fa0405SHaiyang Zhang 8895fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev, 8995fa0405SHaiyang Zhang u32 msg_type, 9095fa0405SHaiyang Zhang u32 msg_len) 9195fa0405SHaiyang Zhang { 9295fa0405SHaiyang Zhang struct rndis_request *request; 9395fa0405SHaiyang Zhang struct rndis_message *rndis_msg; 9495fa0405SHaiyang Zhang struct rndis_set_request *set; 9595fa0405SHaiyang Zhang unsigned long flags; 9695fa0405SHaiyang Zhang 9795fa0405SHaiyang Zhang request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL); 9895fa0405SHaiyang Zhang if (!request) 9995fa0405SHaiyang Zhang return NULL; 10095fa0405SHaiyang Zhang 10195fa0405SHaiyang Zhang init_completion(&request->wait_event); 10295fa0405SHaiyang Zhang 10395fa0405SHaiyang Zhang rndis_msg = &request->request_msg; 10495fa0405SHaiyang Zhang rndis_msg->ndis_msg_type = msg_type; 10595fa0405SHaiyang Zhang rndis_msg->msg_len = msg_len; 10695fa0405SHaiyang Zhang 1075b54dac8SHaiyang Zhang request->pkt.q_idx = 0; 1085b54dac8SHaiyang Zhang 10995fa0405SHaiyang Zhang /* 11095fa0405SHaiyang Zhang * Set the request id. This field is always after the rndis header for 11195fa0405SHaiyang Zhang * request/response packet types so we just used the SetRequest as a 11295fa0405SHaiyang Zhang * template 11395fa0405SHaiyang Zhang */ 11495fa0405SHaiyang Zhang set = &rndis_msg->msg.set_req; 11595fa0405SHaiyang Zhang set->req_id = atomic_inc_return(&dev->new_req_id); 11695fa0405SHaiyang Zhang 11795fa0405SHaiyang Zhang /* Add to the request list */ 11895fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 11995fa0405SHaiyang Zhang list_add_tail(&request->list_ent, &dev->req_list); 12095fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 12195fa0405SHaiyang Zhang 12295fa0405SHaiyang Zhang return request; 12395fa0405SHaiyang Zhang } 12495fa0405SHaiyang Zhang 12595fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev, 12695fa0405SHaiyang Zhang struct rndis_request *req) 12795fa0405SHaiyang Zhang { 12895fa0405SHaiyang Zhang unsigned long flags; 12995fa0405SHaiyang Zhang 13095fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 13195fa0405SHaiyang Zhang list_del(&req->list_ent); 13295fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 13395fa0405SHaiyang Zhang 13495fa0405SHaiyang Zhang kfree(req); 13595fa0405SHaiyang Zhang } 13695fa0405SHaiyang Zhang 13779cf1baeSStephen Hemminger static void dump_rndis_message(struct net_device *netdev, 138dc54a08cSstephen hemminger const struct rndis_message *rndis_msg) 13995fa0405SHaiyang Zhang { 14095fa0405SHaiyang Zhang switch (rndis_msg->ndis_msg_type) { 14151491167SLinus Walleij case RNDIS_MSG_PACKET: 14251491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, " 14395fa0405SHaiyang Zhang "data offset %u data len %u, # oob %u, " 14495fa0405SHaiyang Zhang "oob offset %u, oob len %u, pkt offset %u, " 14595fa0405SHaiyang Zhang "pkt len %u\n", 14695fa0405SHaiyang Zhang rndis_msg->msg_len, 14795fa0405SHaiyang Zhang rndis_msg->msg.pkt.data_offset, 14895fa0405SHaiyang Zhang rndis_msg->msg.pkt.data_len, 14995fa0405SHaiyang Zhang rndis_msg->msg.pkt.num_oob_data_elements, 15095fa0405SHaiyang Zhang rndis_msg->msg.pkt.oob_data_offset, 15195fa0405SHaiyang Zhang rndis_msg->msg.pkt.oob_data_len, 15295fa0405SHaiyang Zhang rndis_msg->msg.pkt.per_pkt_info_offset, 15395fa0405SHaiyang Zhang rndis_msg->msg.pkt.per_pkt_info_len); 15495fa0405SHaiyang Zhang break; 15595fa0405SHaiyang Zhang 15651491167SLinus Walleij case RNDIS_MSG_INIT_C: 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, 16295fa0405SHaiyang Zhang rndis_msg->msg.init_complete.req_id, 16395fa0405SHaiyang Zhang rndis_msg->msg.init_complete.status, 16495fa0405SHaiyang Zhang rndis_msg->msg.init_complete.major_ver, 16595fa0405SHaiyang Zhang rndis_msg->msg.init_complete.minor_ver, 16695fa0405SHaiyang Zhang rndis_msg->msg.init_complete.dev_flags, 16795fa0405SHaiyang Zhang rndis_msg->msg.init_complete.max_xfer_size, 16895fa0405SHaiyang Zhang rndis_msg->msg.init_complete. 16995fa0405SHaiyang Zhang max_pkt_per_msg, 17095fa0405SHaiyang Zhang rndis_msg->msg.init_complete. 17195fa0405SHaiyang Zhang pkt_alignment_factor); 17295fa0405SHaiyang Zhang break; 17395fa0405SHaiyang Zhang 17451491167SLinus Walleij case RNDIS_MSG_QUERY_C: 17551491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_QUERY_C " 17695fa0405SHaiyang Zhang "(len %u, id 0x%x, status 0x%x, buf len %u, " 17795fa0405SHaiyang Zhang "buf offset %u)\n", 17895fa0405SHaiyang Zhang rndis_msg->msg_len, 17995fa0405SHaiyang Zhang rndis_msg->msg.query_complete.req_id, 18095fa0405SHaiyang Zhang rndis_msg->msg.query_complete.status, 18195fa0405SHaiyang Zhang rndis_msg->msg.query_complete. 18295fa0405SHaiyang Zhang info_buflen, 18395fa0405SHaiyang Zhang rndis_msg->msg.query_complete. 18495fa0405SHaiyang Zhang info_buf_offset); 18595fa0405SHaiyang Zhang break; 18695fa0405SHaiyang Zhang 18751491167SLinus Walleij case RNDIS_MSG_SET_C: 18895fa0405SHaiyang Zhang netdev_dbg(netdev, 18951491167SLinus Walleij "RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n", 19095fa0405SHaiyang Zhang rndis_msg->msg_len, 19195fa0405SHaiyang Zhang rndis_msg->msg.set_complete.req_id, 19295fa0405SHaiyang Zhang rndis_msg->msg.set_complete.status); 19395fa0405SHaiyang Zhang break; 19495fa0405SHaiyang Zhang 19551491167SLinus Walleij case RNDIS_MSG_INDICATE: 19651491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_INDICATE " 19795fa0405SHaiyang Zhang "(len %u, status 0x%x, buf len %u, buf offset %u)\n", 19895fa0405SHaiyang Zhang rndis_msg->msg_len, 19995fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status, 20095fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status_buflen, 20195fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status_buf_offset); 20295fa0405SHaiyang Zhang break; 20395fa0405SHaiyang Zhang 20495fa0405SHaiyang Zhang default: 20595fa0405SHaiyang Zhang netdev_dbg(netdev, "0x%x (len %u)\n", 20695fa0405SHaiyang Zhang rndis_msg->ndis_msg_type, 20795fa0405SHaiyang Zhang rndis_msg->msg_len); 20895fa0405SHaiyang Zhang break; 20995fa0405SHaiyang Zhang } 21095fa0405SHaiyang Zhang } 21195fa0405SHaiyang Zhang 21295fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev, 21395fa0405SHaiyang Zhang struct rndis_request *req) 21495fa0405SHaiyang Zhang { 21595fa0405SHaiyang Zhang struct hv_netvsc_packet *packet; 216b08cc791SKY Srinivasan struct hv_page_buffer page_buf[2]; 217a9f2e2d6SKY Srinivasan struct hv_page_buffer *pb = page_buf; 2183d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); 21902b6de01Sstephen hemminger int ret; 22095fa0405SHaiyang Zhang 22195fa0405SHaiyang Zhang /* Setup the packet to send it */ 22295fa0405SHaiyang Zhang packet = &req->pkt; 22395fa0405SHaiyang Zhang 22495fa0405SHaiyang Zhang packet->total_data_buflen = req->request_msg.msg_len; 22595fa0405SHaiyang Zhang packet->page_buf_cnt = 1; 22695fa0405SHaiyang Zhang 227a9f2e2d6SKY Srinivasan pb[0].pfn = virt_to_phys(&req->request_msg) >> 22895fa0405SHaiyang Zhang PAGE_SHIFT; 229a9f2e2d6SKY Srinivasan pb[0].len = req->request_msg.msg_len; 230a9f2e2d6SKY Srinivasan pb[0].offset = 23195fa0405SHaiyang Zhang (unsigned long)&req->request_msg & (PAGE_SIZE - 1); 23295fa0405SHaiyang Zhang 23399e3fcfaSHaiyang Zhang /* Add one page_buf when request_msg crossing page boundary */ 234a9f2e2d6SKY Srinivasan if (pb[0].offset + pb[0].len > PAGE_SIZE) { 23599e3fcfaSHaiyang Zhang packet->page_buf_cnt++; 236a9f2e2d6SKY Srinivasan pb[0].len = PAGE_SIZE - 237a9f2e2d6SKY Srinivasan pb[0].offset; 238a9f2e2d6SKY Srinivasan pb[1].pfn = virt_to_phys((void *)&req->request_msg 239a9f2e2d6SKY Srinivasan + pb[0].len) >> PAGE_SHIFT; 240a9f2e2d6SKY Srinivasan pb[1].offset = 0; 241a9f2e2d6SKY Srinivasan pb[1].len = req->request_msg.msg_len - 242a9f2e2d6SKY Srinivasan pb[0].len; 24399e3fcfaSHaiyang Zhang } 24499e3fcfaSHaiyang Zhang 245867047c4Sstephen hemminger rcu_read_lock_bh(); 24602b6de01Sstephen hemminger ret = netvsc_send(net_device_ctx, packet, NULL, pb, NULL); 247867047c4Sstephen hemminger rcu_read_unlock_bh(); 248867047c4Sstephen hemminger 24995fa0405SHaiyang Zhang return ret; 25095fa0405SHaiyang Zhang } 25195fa0405SHaiyang Zhang 2521b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev, 2531b07da51SHaiyang Zhang struct rndis_request *request) 2541b07da51SHaiyang Zhang { 2551b07da51SHaiyang Zhang u32 link_status; 2561b07da51SHaiyang Zhang struct rndis_query_complete *query_complete; 2571b07da51SHaiyang Zhang 2581b07da51SHaiyang Zhang query_complete = &request->response_msg.msg.query_complete; 2591b07da51SHaiyang Zhang 2601b07da51SHaiyang Zhang if (query_complete->status == RNDIS_STATUS_SUCCESS && 2611b07da51SHaiyang Zhang query_complete->info_buflen == sizeof(u32)) { 2621b07da51SHaiyang Zhang memcpy(&link_status, (void *)((unsigned long)query_complete + 2631b07da51SHaiyang Zhang query_complete->info_buf_offset), sizeof(u32)); 2641b07da51SHaiyang Zhang rdev->link_state = link_status != 0; 2651b07da51SHaiyang Zhang } 2661b07da51SHaiyang Zhang } 2671b07da51SHaiyang Zhang 26895fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev, 26995fa0405SHaiyang Zhang struct rndis_message *resp) 27095fa0405SHaiyang Zhang { 27195fa0405SHaiyang Zhang struct rndis_request *request = NULL; 27295fa0405SHaiyang Zhang bool found = false; 27395fa0405SHaiyang Zhang unsigned long flags; 2743d541ac5SVitaly Kuznetsov struct net_device *ndev = dev->ndev; 27595fa0405SHaiyang Zhang 27695fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 27795fa0405SHaiyang Zhang list_for_each_entry(request, &dev->req_list, list_ent) { 27895fa0405SHaiyang Zhang /* 27995fa0405SHaiyang Zhang * All request/response message contains RequestId as the 1st 28095fa0405SHaiyang Zhang * field 28195fa0405SHaiyang Zhang */ 28295fa0405SHaiyang Zhang if (request->request_msg.msg.init_req.req_id 28395fa0405SHaiyang Zhang == resp->msg.init_complete.req_id) { 28495fa0405SHaiyang Zhang found = true; 28595fa0405SHaiyang Zhang break; 28695fa0405SHaiyang Zhang } 28795fa0405SHaiyang Zhang } 28895fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 28995fa0405SHaiyang Zhang 29095fa0405SHaiyang Zhang if (found) { 291a3a6cab5SHaiyang Zhang if (resp->msg_len <= 292a3a6cab5SHaiyang Zhang sizeof(struct rndis_message) + RNDIS_EXT_LEN) { 29395fa0405SHaiyang Zhang memcpy(&request->response_msg, resp, 29495fa0405SHaiyang Zhang resp->msg_len); 2951b07da51SHaiyang Zhang if (request->request_msg.ndis_msg_type == 2961b07da51SHaiyang Zhang RNDIS_MSG_QUERY && request->request_msg.msg. 2971b07da51SHaiyang Zhang query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS) 2981b07da51SHaiyang Zhang rndis_set_link_state(dev, request); 29995fa0405SHaiyang Zhang } else { 30095fa0405SHaiyang Zhang netdev_err(ndev, 30195fa0405SHaiyang Zhang "rndis response buffer overflow " 30295fa0405SHaiyang Zhang "detected (size %u max %zu)\n", 30395fa0405SHaiyang Zhang resp->msg_len, 30486eedaccSKY Srinivasan sizeof(struct rndis_message)); 30595fa0405SHaiyang Zhang 30695fa0405SHaiyang Zhang if (resp->ndis_msg_type == 30751491167SLinus Walleij RNDIS_MSG_RESET_C) { 30895fa0405SHaiyang Zhang /* does not have a request id field */ 30995fa0405SHaiyang Zhang request->response_msg.msg.reset_complete. 310007e5c8eSLinus Walleij status = RNDIS_STATUS_BUFFER_OVERFLOW; 31195fa0405SHaiyang Zhang } else { 31295fa0405SHaiyang Zhang request->response_msg.msg. 31395fa0405SHaiyang Zhang init_complete.status = 314007e5c8eSLinus Walleij RNDIS_STATUS_BUFFER_OVERFLOW; 31595fa0405SHaiyang Zhang } 31695fa0405SHaiyang Zhang } 31795fa0405SHaiyang Zhang 31895fa0405SHaiyang Zhang complete(&request->wait_event); 31995fa0405SHaiyang Zhang } else { 32095fa0405SHaiyang Zhang netdev_err(ndev, 32195fa0405SHaiyang Zhang "no rndis request found for this response " 32295fa0405SHaiyang Zhang "(id 0x%x res type 0x%x)\n", 32395fa0405SHaiyang Zhang resp->msg.init_complete.req_id, 32495fa0405SHaiyang Zhang resp->ndis_msg_type); 32595fa0405SHaiyang Zhang } 32695fa0405SHaiyang Zhang } 32795fa0405SHaiyang Zhang 3281f5f3a75SHaiyang Zhang /* 3291f5f3a75SHaiyang Zhang * Get the Per-Packet-Info with the specified type 3301f5f3a75SHaiyang Zhang * return NULL if not found. 3311f5f3a75SHaiyang Zhang */ 3321f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) 3331f5f3a75SHaiyang Zhang { 3341f5f3a75SHaiyang Zhang struct rndis_per_packet_info *ppi; 3351f5f3a75SHaiyang Zhang int len; 3361f5f3a75SHaiyang Zhang 3371f5f3a75SHaiyang Zhang if (rpkt->per_pkt_info_offset == 0) 3381f5f3a75SHaiyang Zhang return NULL; 3391f5f3a75SHaiyang Zhang 3401f5f3a75SHaiyang Zhang ppi = (struct rndis_per_packet_info *)((ulong)rpkt + 3411f5f3a75SHaiyang Zhang rpkt->per_pkt_info_offset); 3421f5f3a75SHaiyang Zhang len = rpkt->per_pkt_info_len; 3431f5f3a75SHaiyang Zhang 3441f5f3a75SHaiyang Zhang while (len > 0) { 3451f5f3a75SHaiyang Zhang if (ppi->type == type) 3461f5f3a75SHaiyang Zhang return (void *)((ulong)ppi + ppi->ppi_offset); 3471f5f3a75SHaiyang Zhang len -= ppi->size; 3481f5f3a75SHaiyang Zhang ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size); 3491f5f3a75SHaiyang Zhang } 3501f5f3a75SHaiyang Zhang 3511f5f3a75SHaiyang Zhang return NULL; 3521f5f3a75SHaiyang Zhang } 3531f5f3a75SHaiyang Zhang 354dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev, 355dc54a08cSstephen hemminger struct rndis_device *dev, 35695fa0405SHaiyang Zhang struct rndis_message *msg, 357dc54a08cSstephen hemminger struct vmbus_channel *channel, 358dc54a08cSstephen hemminger void *data, u32 data_buflen) 35995fa0405SHaiyang Zhang { 360dc54a08cSstephen hemminger struct rndis_packet *rndis_pkt = &msg->msg.pkt; 361dc54a08cSstephen hemminger const struct ndis_tcp_ip_checksum_info *csum_info; 362dc54a08cSstephen hemminger const struct ndis_pkt_8021q_info *vlan; 36395fa0405SHaiyang Zhang u32 data_offset; 36495fa0405SHaiyang Zhang 36595fa0405SHaiyang Zhang /* Remove the rndis header and pass it back up the stack */ 36695fa0405SHaiyang Zhang data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; 36795fa0405SHaiyang Zhang 368dc54a08cSstephen hemminger data_buflen -= data_offset; 3694b8a8bc9SWei Yongjun 3704b8a8bc9SWei Yongjun /* 3714b8a8bc9SWei Yongjun * Make sure we got a valid RNDIS message, now total_data_buflen 3724b8a8bc9SWei Yongjun * should be the data packet size plus the trailer padding size 3734b8a8bc9SWei Yongjun */ 374dc54a08cSstephen hemminger if (unlikely(data_buflen < rndis_pkt->data_len)) { 3753d541ac5SVitaly Kuznetsov netdev_err(dev->ndev, "rndis message buffer " 3764b8a8bc9SWei Yongjun "overflow detected (got %u, min %u)" 3774b8a8bc9SWei Yongjun "...dropping this message!\n", 378dc54a08cSstephen hemminger data_buflen, rndis_pkt->data_len); 37910082f98SKY Srinivasan return NVSP_STAT_FAIL; 3804b8a8bc9SWei Yongjun } 3814b8a8bc9SWei Yongjun 382dc54a08cSstephen hemminger vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); 383dc54a08cSstephen hemminger 3844b8a8bc9SWei Yongjun /* 3854b8a8bc9SWei Yongjun * Remove the rndis trailer padding from rndis packet message 3864b8a8bc9SWei Yongjun * rndis_pkt->data_len tell us the real data length, we only copy 3874b8a8bc9SWei Yongjun * the data packet to the stack, without the rndis trailer padding 3884b8a8bc9SWei Yongjun */ 389dc54a08cSstephen hemminger data = (void *)((unsigned long)data + data_offset); 390e3d605edSKY Srinivasan csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); 391dc54a08cSstephen hemminger return netvsc_recv_callback(ndev, channel, 392dc54a08cSstephen hemminger data, rndis_pkt->data_len, 393dc54a08cSstephen hemminger csum_info, vlan); 39495fa0405SHaiyang Zhang } 39595fa0405SHaiyang Zhang 396dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev, 397dc54a08cSstephen hemminger struct netvsc_device *net_dev, 398dc54a08cSstephen hemminger struct vmbus_channel *channel, 399dc54a08cSstephen hemminger void *data, u32 buflen) 40095fa0405SHaiyang Zhang { 4013d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(ndev); 402dc54a08cSstephen hemminger struct rndis_device *rndis_dev = net_dev->extension; 403dc54a08cSstephen hemminger struct rndis_message *rndis_msg = data; 40495fa0405SHaiyang Zhang 40595fa0405SHaiyang Zhang /* Make sure the rndis device state is initialized */ 406dc54a08cSstephen hemminger if (unlikely(!rndis_dev)) { 407b5eb819dSVitaly Kuznetsov netif_dbg(net_device_ctx, rx_err, ndev, 408dc54a08cSstephen hemminger "got rndis message but no rndis device!\n"); 409dc54a08cSstephen hemminger return NVSP_STAT_FAIL; 41095fa0405SHaiyang Zhang } 41195fa0405SHaiyang Zhang 412dc54a08cSstephen hemminger if (unlikely(rndis_dev->state == RNDIS_DEV_UNINITIALIZED)) { 413b5eb819dSVitaly Kuznetsov netif_dbg(net_device_ctx, rx_err, ndev, 414dc54a08cSstephen hemminger "got rndis message uninitialized\n"); 415dc54a08cSstephen hemminger return NVSP_STAT_FAIL; 41695fa0405SHaiyang Zhang } 41795fa0405SHaiyang Zhang 418dc54a08cSstephen hemminger if (netif_msg_rx_status(net_device_ctx)) 41979cf1baeSStephen Hemminger dump_rndis_message(ndev, rndis_msg); 42095fa0405SHaiyang Zhang 421ef31bef6SHaiyang Zhang switch (rndis_msg->ndis_msg_type) { 42251491167SLinus Walleij case RNDIS_MSG_PACKET: 423dc54a08cSstephen hemminger return rndis_filter_receive_data(ndev, rndis_dev, rndis_msg, 424dc54a08cSstephen hemminger channel, data, buflen); 42551491167SLinus Walleij case RNDIS_MSG_INIT_C: 42651491167SLinus Walleij case RNDIS_MSG_QUERY_C: 42751491167SLinus Walleij case RNDIS_MSG_SET_C: 42895fa0405SHaiyang Zhang /* completion msgs */ 429ef31bef6SHaiyang Zhang rndis_filter_receive_response(rndis_dev, rndis_msg); 43095fa0405SHaiyang Zhang break; 43195fa0405SHaiyang Zhang 43251491167SLinus Walleij case RNDIS_MSG_INDICATE: 43395fa0405SHaiyang Zhang /* notification msgs */ 43479cf1baeSStephen Hemminger netvsc_linkstatus_callback(ndev, rndis_msg); 43595fa0405SHaiyang Zhang break; 43695fa0405SHaiyang Zhang default: 43795fa0405SHaiyang Zhang netdev_err(ndev, 43895fa0405SHaiyang Zhang "unhandled rndis message (type %u len %u)\n", 439ef31bef6SHaiyang Zhang rndis_msg->ndis_msg_type, 440ef31bef6SHaiyang Zhang rndis_msg->msg_len); 44195fa0405SHaiyang Zhang break; 44295fa0405SHaiyang Zhang } 44395fa0405SHaiyang Zhang 444dc54a08cSstephen hemminger return 0; 44595fa0405SHaiyang Zhang } 44695fa0405SHaiyang Zhang 447867047c4Sstephen hemminger static int rndis_filter_query_device(struct rndis_device *dev, 448867047c4Sstephen hemminger struct netvsc_device *nvdev, 449867047c4Sstephen hemminger u32 oid, void *result, u32 *result_size) 45095fa0405SHaiyang Zhang { 45195fa0405SHaiyang Zhang struct rndis_request *request; 45295fa0405SHaiyang Zhang u32 inresult_size = *result_size; 45395fa0405SHaiyang Zhang struct rndis_query_request *query; 45495fa0405SHaiyang Zhang struct rndis_query_complete *query_complete; 45595fa0405SHaiyang Zhang int ret = 0; 45695fa0405SHaiyang Zhang 45795fa0405SHaiyang Zhang if (!result) 45895fa0405SHaiyang Zhang return -EINVAL; 45995fa0405SHaiyang Zhang 46095fa0405SHaiyang Zhang *result_size = 0; 46151491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_QUERY, 46295fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_query_request)); 46395fa0405SHaiyang Zhang if (!request) { 46495fa0405SHaiyang Zhang ret = -ENOMEM; 46595fa0405SHaiyang Zhang goto cleanup; 46695fa0405SHaiyang Zhang } 46795fa0405SHaiyang Zhang 46895fa0405SHaiyang Zhang /* Setup the rndis query */ 46995fa0405SHaiyang Zhang query = &request->request_msg.msg.query_req; 47095fa0405SHaiyang Zhang query->oid = oid; 47195fa0405SHaiyang Zhang query->info_buf_offset = sizeof(struct rndis_query_request); 47295fa0405SHaiyang Zhang query->info_buflen = 0; 47395fa0405SHaiyang Zhang query->dev_vc_handle = 0; 47495fa0405SHaiyang Zhang 47523312a3bSstephen hemminger if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) { 47623312a3bSstephen hemminger struct ndis_offload *hwcaps; 47723312a3bSstephen hemminger u32 nvsp_version = nvdev->nvsp_version; 47823312a3bSstephen hemminger u8 ndis_rev; 47923312a3bSstephen hemminger size_t size; 48023312a3bSstephen hemminger 48123312a3bSstephen hemminger if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) { 48223312a3bSstephen hemminger ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3; 48323312a3bSstephen hemminger size = NDIS_OFFLOAD_SIZE; 48423312a3bSstephen hemminger } else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) { 48523312a3bSstephen hemminger ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2; 48623312a3bSstephen hemminger size = NDIS_OFFLOAD_SIZE_6_1; 48723312a3bSstephen hemminger } else { 48823312a3bSstephen hemminger ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1; 48923312a3bSstephen hemminger size = NDIS_OFFLOAD_SIZE_6_0; 49023312a3bSstephen hemminger } 49123312a3bSstephen hemminger 49223312a3bSstephen hemminger request->request_msg.msg_len += size; 49323312a3bSstephen hemminger query->info_buflen = size; 49423312a3bSstephen hemminger hwcaps = (struct ndis_offload *) 49523312a3bSstephen hemminger ((unsigned long)query + query->info_buf_offset); 49623312a3bSstephen hemminger 49723312a3bSstephen hemminger hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD; 49823312a3bSstephen hemminger hwcaps->header.revision = ndis_rev; 49923312a3bSstephen hemminger hwcaps->header.size = size; 50023312a3bSstephen hemminger 50123312a3bSstephen hemminger } else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) { 5025b54dac8SHaiyang Zhang struct ndis_recv_scale_cap *cap; 5035b54dac8SHaiyang Zhang 5045b54dac8SHaiyang Zhang request->request_msg.msg_len += 5055b54dac8SHaiyang Zhang sizeof(struct ndis_recv_scale_cap); 5065b54dac8SHaiyang Zhang query->info_buflen = sizeof(struct ndis_recv_scale_cap); 5075b54dac8SHaiyang Zhang cap = (struct ndis_recv_scale_cap *)((unsigned long)query + 5085b54dac8SHaiyang Zhang query->info_buf_offset); 5095b54dac8SHaiyang Zhang cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES; 5105b54dac8SHaiyang Zhang cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2; 5115b54dac8SHaiyang Zhang cap->hdr.size = sizeof(struct ndis_recv_scale_cap); 5125b54dac8SHaiyang Zhang } 5135b54dac8SHaiyang Zhang 51495fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 51595fa0405SHaiyang Zhang if (ret != 0) 51695fa0405SHaiyang Zhang goto cleanup; 51795fa0405SHaiyang Zhang 5185362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 51995fa0405SHaiyang Zhang 52095fa0405SHaiyang Zhang /* Copy the response back */ 52195fa0405SHaiyang Zhang query_complete = &request->response_msg.msg.query_complete; 52295fa0405SHaiyang Zhang 52395fa0405SHaiyang Zhang if (query_complete->info_buflen > inresult_size) { 52495fa0405SHaiyang Zhang ret = -1; 52595fa0405SHaiyang Zhang goto cleanup; 52695fa0405SHaiyang Zhang } 52795fa0405SHaiyang Zhang 52895fa0405SHaiyang Zhang memcpy(result, 52995fa0405SHaiyang Zhang (void *)((unsigned long)query_complete + 53095fa0405SHaiyang Zhang query_complete->info_buf_offset), 53195fa0405SHaiyang Zhang query_complete->info_buflen); 53295fa0405SHaiyang Zhang 53395fa0405SHaiyang Zhang *result_size = query_complete->info_buflen; 53495fa0405SHaiyang Zhang 53595fa0405SHaiyang Zhang cleanup: 53695fa0405SHaiyang Zhang if (request) 53795fa0405SHaiyang Zhang put_rndis_request(dev, request); 53895fa0405SHaiyang Zhang 53995fa0405SHaiyang Zhang return ret; 54095fa0405SHaiyang Zhang } 54195fa0405SHaiyang Zhang 54223312a3bSstephen hemminger /* Get the hardware offload capabilities */ 54323312a3bSstephen hemminger static int 544867047c4Sstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device, 545867047c4Sstephen hemminger struct ndis_offload *caps) 54623312a3bSstephen hemminger { 54723312a3bSstephen hemminger u32 caps_len = sizeof(*caps); 54823312a3bSstephen hemminger int ret; 54923312a3bSstephen hemminger 55023312a3bSstephen hemminger memset(caps, 0, sizeof(*caps)); 55123312a3bSstephen hemminger 552867047c4Sstephen hemminger ret = rndis_filter_query_device(dev, net_device, 55323312a3bSstephen hemminger OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES, 55423312a3bSstephen hemminger caps, &caps_len); 55523312a3bSstephen hemminger if (ret) 55623312a3bSstephen hemminger return ret; 55723312a3bSstephen hemminger 55823312a3bSstephen hemminger if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) { 55923312a3bSstephen hemminger netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n", 56023312a3bSstephen hemminger caps->header.type); 56123312a3bSstephen hemminger return -EINVAL; 56223312a3bSstephen hemminger } 56323312a3bSstephen hemminger 56423312a3bSstephen hemminger if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) { 56523312a3bSstephen hemminger netdev_warn(dev->ndev, "invalid NDIS objrev %x\n", 56623312a3bSstephen hemminger caps->header.revision); 56723312a3bSstephen hemminger return -EINVAL; 56823312a3bSstephen hemminger } 56923312a3bSstephen hemminger 57023312a3bSstephen hemminger if (caps->header.size > caps_len || 57123312a3bSstephen hemminger caps->header.size < NDIS_OFFLOAD_SIZE_6_0) { 57223312a3bSstephen hemminger netdev_warn(dev->ndev, 57323312a3bSstephen hemminger "invalid NDIS objsize %u, data size %u\n", 57423312a3bSstephen hemminger caps->header.size, caps_len); 57523312a3bSstephen hemminger return -EINVAL; 57623312a3bSstephen hemminger } 57723312a3bSstephen hemminger 57823312a3bSstephen hemminger return 0; 57923312a3bSstephen hemminger } 58023312a3bSstephen hemminger 581867047c4Sstephen hemminger static int rndis_filter_query_device_mac(struct rndis_device *dev, 582867047c4Sstephen hemminger struct netvsc_device *net_device) 58395fa0405SHaiyang Zhang { 58495fa0405SHaiyang Zhang u32 size = ETH_ALEN; 58595fa0405SHaiyang Zhang 586867047c4Sstephen hemminger return rndis_filter_query_device(dev, net_device, 58795fa0405SHaiyang Zhang RNDIS_OID_802_3_PERMANENT_ADDRESS, 58895fa0405SHaiyang Zhang dev->hw_mac_adr, &size); 58995fa0405SHaiyang Zhang } 59095fa0405SHaiyang Zhang 5911ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress" 5921ce09e89SHaiyang Zhang #define NWADR_STRLEN 14 5931ce09e89SHaiyang Zhang 594867047c4Sstephen hemminger int rndis_filter_set_device_mac(struct netvsc_device *nvdev, 595867047c4Sstephen hemminger const char *mac) 5961ce09e89SHaiyang Zhang { 5971ce09e89SHaiyang Zhang struct rndis_device *rdev = nvdev->extension; 5981ce09e89SHaiyang Zhang struct rndis_request *request; 5991ce09e89SHaiyang Zhang struct rndis_set_request *set; 6001ce09e89SHaiyang Zhang struct rndis_config_parameter_info *cpi; 6011ce09e89SHaiyang Zhang wchar_t *cfg_nwadr, *cfg_mac; 6021ce09e89SHaiyang Zhang struct rndis_set_complete *set_complete; 6031ce09e89SHaiyang Zhang char macstr[2*ETH_ALEN+1]; 6041ce09e89SHaiyang Zhang u32 extlen = sizeof(struct rndis_config_parameter_info) + 6051ce09e89SHaiyang Zhang 2*NWADR_STRLEN + 4*ETH_ALEN; 606999028ccSNicholas Mc Guire int ret; 6071ce09e89SHaiyang Zhang 6081ce09e89SHaiyang Zhang request = get_rndis_request(rdev, RNDIS_MSG_SET, 6091ce09e89SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 6101ce09e89SHaiyang Zhang if (!request) 6111ce09e89SHaiyang Zhang return -ENOMEM; 6121ce09e89SHaiyang Zhang 6131ce09e89SHaiyang Zhang set = &request->request_msg.msg.set_req; 6141ce09e89SHaiyang Zhang set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER; 6151ce09e89SHaiyang Zhang set->info_buflen = extlen; 6161ce09e89SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 6171ce09e89SHaiyang Zhang set->dev_vc_handle = 0; 6181ce09e89SHaiyang Zhang 6191ce09e89SHaiyang Zhang cpi = (struct rndis_config_parameter_info *)((ulong)set + 6201ce09e89SHaiyang Zhang set->info_buf_offset); 6211ce09e89SHaiyang Zhang cpi->parameter_name_offset = 6221ce09e89SHaiyang Zhang sizeof(struct rndis_config_parameter_info); 6231ce09e89SHaiyang Zhang /* Multiply by 2 because host needs 2 bytes (utf16) for each char */ 6241ce09e89SHaiyang Zhang cpi->parameter_name_length = 2*NWADR_STRLEN; 6251ce09e89SHaiyang Zhang cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING; 6261ce09e89SHaiyang Zhang cpi->parameter_value_offset = 6271ce09e89SHaiyang Zhang cpi->parameter_name_offset + cpi->parameter_name_length; 6281ce09e89SHaiyang Zhang /* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */ 6291ce09e89SHaiyang Zhang cpi->parameter_value_length = 4*ETH_ALEN; 6301ce09e89SHaiyang Zhang 6311ce09e89SHaiyang Zhang cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset); 6321ce09e89SHaiyang Zhang cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset); 6331ce09e89SHaiyang Zhang ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN, 6341ce09e89SHaiyang Zhang cfg_nwadr, NWADR_STRLEN); 6351ce09e89SHaiyang Zhang if (ret < 0) 6361ce09e89SHaiyang Zhang goto cleanup; 6371ce09e89SHaiyang Zhang snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac); 6381ce09e89SHaiyang Zhang ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN, 6391ce09e89SHaiyang Zhang cfg_mac, 2*ETH_ALEN); 6401ce09e89SHaiyang Zhang if (ret < 0) 6411ce09e89SHaiyang Zhang goto cleanup; 6421ce09e89SHaiyang Zhang 6431ce09e89SHaiyang Zhang ret = rndis_filter_send_request(rdev, request); 6441ce09e89SHaiyang Zhang if (ret != 0) 6451ce09e89SHaiyang Zhang goto cleanup; 6461ce09e89SHaiyang Zhang 6475362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 6485362855aSVitaly Kuznetsov 6491ce09e89SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 650867047c4Sstephen hemminger if (set_complete->status != RNDIS_STATUS_SUCCESS) 651867047c4Sstephen hemminger ret = -EIO; 6521ce09e89SHaiyang Zhang 6531ce09e89SHaiyang Zhang cleanup: 6541ce09e89SHaiyang Zhang put_rndis_request(rdev, request); 6551ce09e89SHaiyang Zhang return ret; 6561ce09e89SHaiyang Zhang } 6571ce09e89SHaiyang Zhang 658da19fcd0SLad, Prabhakar static int 659426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev, 6609749fed5Sstephen hemminger struct netvsc_device *nvdev, 6614a0e70aeSKY Srinivasan struct ndis_offload_params *req_offloads) 6624a0e70aeSKY Srinivasan { 6634a0e70aeSKY Srinivasan struct rndis_device *rdev = nvdev->extension; 6644a0e70aeSKY Srinivasan struct rndis_request *request; 6654a0e70aeSKY Srinivasan struct rndis_set_request *set; 6664a0e70aeSKY Srinivasan struct ndis_offload_params *offload_params; 6674a0e70aeSKY Srinivasan struct rndis_set_complete *set_complete; 6684a0e70aeSKY Srinivasan u32 extlen = sizeof(struct ndis_offload_params); 669999028ccSNicholas Mc Guire int ret; 670af9893a3SKY Srinivasan u32 vsp_version = nvdev->nvsp_version; 671af9893a3SKY Srinivasan 672af9893a3SKY Srinivasan if (vsp_version <= NVSP_PROTOCOL_VERSION_4) { 673af9893a3SKY Srinivasan extlen = VERSION_4_OFFLOAD_SIZE; 674af9893a3SKY Srinivasan /* On NVSP_PROTOCOL_VERSION_4 and below, we do not support 675af9893a3SKY Srinivasan * UDP checksum offload. 676af9893a3SKY Srinivasan */ 677af9893a3SKY Srinivasan req_offloads->udp_ip_v4_csum = 0; 678af9893a3SKY Srinivasan req_offloads->udp_ip_v6_csum = 0; 679af9893a3SKY Srinivasan } 6804a0e70aeSKY Srinivasan 6814a0e70aeSKY Srinivasan request = get_rndis_request(rdev, RNDIS_MSG_SET, 6824a0e70aeSKY Srinivasan RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 6834a0e70aeSKY Srinivasan if (!request) 6844a0e70aeSKY Srinivasan return -ENOMEM; 6854a0e70aeSKY Srinivasan 6864a0e70aeSKY Srinivasan set = &request->request_msg.msg.set_req; 6874a0e70aeSKY Srinivasan set->oid = OID_TCP_OFFLOAD_PARAMETERS; 6884a0e70aeSKY Srinivasan set->info_buflen = extlen; 6894a0e70aeSKY Srinivasan set->info_buf_offset = sizeof(struct rndis_set_request); 6904a0e70aeSKY Srinivasan set->dev_vc_handle = 0; 6914a0e70aeSKY Srinivasan 6924a0e70aeSKY Srinivasan offload_params = (struct ndis_offload_params *)((ulong)set + 6934a0e70aeSKY Srinivasan set->info_buf_offset); 6944a0e70aeSKY Srinivasan *offload_params = *req_offloads; 6954a0e70aeSKY Srinivasan offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT; 6964a0e70aeSKY Srinivasan offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3; 6974a0e70aeSKY Srinivasan offload_params->header.size = extlen; 6984a0e70aeSKY Srinivasan 6994a0e70aeSKY Srinivasan ret = rndis_filter_send_request(rdev, request); 7004a0e70aeSKY Srinivasan if (ret != 0) 7014a0e70aeSKY Srinivasan goto cleanup; 7024a0e70aeSKY Srinivasan 7035362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 7044a0e70aeSKY Srinivasan set_complete = &request->response_msg.msg.set_complete; 7054a0e70aeSKY Srinivasan if (set_complete->status != RNDIS_STATUS_SUCCESS) { 706af9893a3SKY Srinivasan netdev_err(ndev, "Fail to set offload on host side:0x%x\n", 7074a0e70aeSKY Srinivasan set_complete->status); 7084a0e70aeSKY Srinivasan ret = -EINVAL; 7094a0e70aeSKY Srinivasan } 7104a0e70aeSKY Srinivasan 7114a0e70aeSKY Srinivasan cleanup: 7124a0e70aeSKY Srinivasan put_rndis_request(rdev, request); 7134a0e70aeSKY Srinivasan return ret; 7144a0e70aeSKY Srinivasan } 7151ce09e89SHaiyang Zhang 716962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev, 717715e2ec5SHaiyang Zhang const u8 *rss_key) 7185b54dac8SHaiyang Zhang { 7193d541ac5SVitaly Kuznetsov struct net_device *ndev = rdev->ndev; 7205b54dac8SHaiyang Zhang struct rndis_request *request; 7215b54dac8SHaiyang Zhang struct rndis_set_request *set; 7225b54dac8SHaiyang Zhang struct rndis_set_complete *set_complete; 7235b54dac8SHaiyang Zhang u32 extlen = sizeof(struct ndis_recv_scale_param) + 724962f3feeSstephen hemminger 4 * ITAB_NUM + NETVSC_HASH_KEYLEN; 7255b54dac8SHaiyang Zhang struct ndis_recv_scale_param *rssp; 7265b54dac8SHaiyang Zhang u32 *itab; 7275b54dac8SHaiyang Zhang u8 *keyp; 728999028ccSNicholas Mc Guire int i, ret; 7295b54dac8SHaiyang Zhang 7305b54dac8SHaiyang Zhang request = get_rndis_request( 7315b54dac8SHaiyang Zhang rdev, RNDIS_MSG_SET, 7325b54dac8SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 7335b54dac8SHaiyang Zhang if (!request) 7345b54dac8SHaiyang Zhang return -ENOMEM; 7355b54dac8SHaiyang Zhang 7365b54dac8SHaiyang Zhang set = &request->request_msg.msg.set_req; 7375b54dac8SHaiyang Zhang set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS; 7385b54dac8SHaiyang Zhang set->info_buflen = extlen; 7395b54dac8SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 7405b54dac8SHaiyang Zhang set->dev_vc_handle = 0; 7415b54dac8SHaiyang Zhang 7425b54dac8SHaiyang Zhang rssp = (struct ndis_recv_scale_param *)(set + 1); 7435b54dac8SHaiyang Zhang rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS; 7445b54dac8SHaiyang Zhang rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2; 7455b54dac8SHaiyang Zhang rssp->hdr.size = sizeof(struct ndis_recv_scale_param); 7465b54dac8SHaiyang Zhang rssp->flag = 0; 7475b54dac8SHaiyang Zhang rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 | 7484c87454aSHaiyang Zhang NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 | 7494c87454aSHaiyang Zhang NDIS_HASH_TCP_IPV6; 7505b54dac8SHaiyang Zhang rssp->indirect_tabsize = 4*ITAB_NUM; 7515b54dac8SHaiyang Zhang rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param); 752962f3feeSstephen hemminger rssp->hashkey_size = NETVSC_HASH_KEYLEN; 7535b54dac8SHaiyang Zhang rssp->kashkey_offset = rssp->indirect_taboffset + 7545b54dac8SHaiyang Zhang rssp->indirect_tabsize; 7555b54dac8SHaiyang Zhang 7565b54dac8SHaiyang Zhang /* Set indirection table entries */ 7575b54dac8SHaiyang Zhang itab = (u32 *)(rssp + 1); 7585b54dac8SHaiyang Zhang for (i = 0; i < ITAB_NUM; i++) 75947371300SHaiyang Zhang itab[i] = rdev->rx_table[i]; 7605b54dac8SHaiyang Zhang 7615b54dac8SHaiyang Zhang /* Set hask key values */ 7625b54dac8SHaiyang Zhang keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset); 763962f3feeSstephen hemminger memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN); 7645b54dac8SHaiyang Zhang 7655b54dac8SHaiyang Zhang ret = rndis_filter_send_request(rdev, request); 7665b54dac8SHaiyang Zhang if (ret != 0) 7675b54dac8SHaiyang Zhang goto cleanup; 7685b54dac8SHaiyang Zhang 7695362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 7705b54dac8SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 771962f3feeSstephen hemminger if (set_complete->status == RNDIS_STATUS_SUCCESS) 772962f3feeSstephen hemminger memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN); 773962f3feeSstephen hemminger else { 7745b54dac8SHaiyang Zhang netdev_err(ndev, "Fail to set RSS parameters:0x%x\n", 7755b54dac8SHaiyang Zhang set_complete->status); 7765b54dac8SHaiyang Zhang ret = -EINVAL; 7775b54dac8SHaiyang Zhang } 7785b54dac8SHaiyang Zhang 7795b54dac8SHaiyang Zhang cleanup: 7805b54dac8SHaiyang Zhang put_rndis_request(rdev, request); 7815b54dac8SHaiyang Zhang return ret; 7825b54dac8SHaiyang Zhang } 7835b54dac8SHaiyang Zhang 784867047c4Sstephen hemminger static int rndis_filter_query_device_link_status(struct rndis_device *dev, 785867047c4Sstephen hemminger struct netvsc_device *net_device) 78695fa0405SHaiyang Zhang { 78795fa0405SHaiyang Zhang u32 size = sizeof(u32); 78895fa0405SHaiyang Zhang u32 link_status; 78995fa0405SHaiyang Zhang 790867047c4Sstephen hemminger return rndis_filter_query_device(dev, net_device, 79195fa0405SHaiyang Zhang RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, 79295fa0405SHaiyang Zhang &link_status, &size); 79395fa0405SHaiyang Zhang } 79495fa0405SHaiyang Zhang 795867047c4Sstephen hemminger static int rndis_filter_query_link_speed(struct rndis_device *dev, 796867047c4Sstephen hemminger struct netvsc_device *net_device) 797b37879e6SHaiyang Zhang { 798b37879e6SHaiyang Zhang u32 size = sizeof(u32); 799b37879e6SHaiyang Zhang u32 link_speed; 800b37879e6SHaiyang Zhang struct net_device_context *ndc; 801b37879e6SHaiyang Zhang int ret; 802b37879e6SHaiyang Zhang 803867047c4Sstephen hemminger ret = rndis_filter_query_device(dev, net_device, 804867047c4Sstephen hemminger RNDIS_OID_GEN_LINK_SPEED, 805b37879e6SHaiyang Zhang &link_speed, &size); 806b37879e6SHaiyang Zhang 807b37879e6SHaiyang Zhang if (!ret) { 808b37879e6SHaiyang Zhang ndc = netdev_priv(dev->ndev); 809b37879e6SHaiyang Zhang 810b37879e6SHaiyang Zhang /* The link speed reported from host is in 100bps unit, so 811b37879e6SHaiyang Zhang * we convert it to Mbps here. 812b37879e6SHaiyang Zhang */ 813b37879e6SHaiyang Zhang ndc->speed = link_speed / 10000; 814b37879e6SHaiyang Zhang } 815b37879e6SHaiyang Zhang 816b37879e6SHaiyang Zhang return ret; 817b37879e6SHaiyang Zhang } 818b37879e6SHaiyang Zhang 8194f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev, 8204f19c0d8Sstephen hemminger u32 new_filter) 82195fa0405SHaiyang Zhang { 82295fa0405SHaiyang Zhang struct rndis_request *request; 82395fa0405SHaiyang Zhang struct rndis_set_request *set; 824999028ccSNicholas Mc Guire int ret; 82595fa0405SHaiyang Zhang 82651491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_SET, 82795fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + 82895fa0405SHaiyang Zhang sizeof(u32)); 829ce12b810Sstephen hemminger if (!request) 830ce12b810Sstephen hemminger return -ENOMEM; 831ce12b810Sstephen hemminger 83295fa0405SHaiyang Zhang 83395fa0405SHaiyang Zhang /* Setup the rndis set */ 83495fa0405SHaiyang Zhang set = &request->request_msg.msg.set_req; 83595fa0405SHaiyang Zhang set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; 83695fa0405SHaiyang Zhang set->info_buflen = sizeof(u32); 83795fa0405SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 83895fa0405SHaiyang Zhang 83995fa0405SHaiyang Zhang memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), 84095fa0405SHaiyang Zhang &new_filter, sizeof(u32)); 84195fa0405SHaiyang Zhang 84295fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 843ce12b810Sstephen hemminger if (ret == 0) 8445362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 84595fa0405SHaiyang Zhang 84695fa0405SHaiyang Zhang put_rndis_request(dev, request); 847ce12b810Sstephen hemminger 84895fa0405SHaiyang Zhang return ret; 84995fa0405SHaiyang Zhang } 85095fa0405SHaiyang Zhang 8514f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w) 8524f19c0d8Sstephen hemminger { 8534f19c0d8Sstephen hemminger struct rndis_device *rdev 8544f19c0d8Sstephen hemminger = container_of(w, struct rndis_device, mcast_work); 8554f19c0d8Sstephen hemminger 8564f19c0d8Sstephen hemminger if (rdev->ndev->flags & IFF_PROMISC) 8574f19c0d8Sstephen hemminger rndis_filter_set_packet_filter(rdev, 8584f19c0d8Sstephen hemminger NDIS_PACKET_TYPE_PROMISCUOUS); 8594f19c0d8Sstephen hemminger else 8604f19c0d8Sstephen hemminger rndis_filter_set_packet_filter(rdev, 8614f19c0d8Sstephen hemminger NDIS_PACKET_TYPE_BROADCAST | 8624f19c0d8Sstephen hemminger NDIS_PACKET_TYPE_ALL_MULTICAST | 8634f19c0d8Sstephen hemminger NDIS_PACKET_TYPE_DIRECTED); 8644f19c0d8Sstephen hemminger } 8654f19c0d8Sstephen hemminger 8664f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev) 8674f19c0d8Sstephen hemminger { 8684f19c0d8Sstephen hemminger struct rndis_device *rdev = nvdev->extension; 8694f19c0d8Sstephen hemminger 8704f19c0d8Sstephen hemminger schedule_work(&rdev->mcast_work); 8714f19c0d8Sstephen hemminger } 8724f19c0d8Sstephen hemminger 873867047c4Sstephen hemminger static int rndis_filter_init_device(struct rndis_device *dev, 874867047c4Sstephen hemminger struct netvsc_device *nvdev) 87595fa0405SHaiyang Zhang { 87695fa0405SHaiyang Zhang struct rndis_request *request; 87795fa0405SHaiyang Zhang struct rndis_initialize_request *init; 87895fa0405SHaiyang Zhang struct rndis_initialize_complete *init_complete; 87995fa0405SHaiyang Zhang u32 status; 880999028ccSNicholas Mc Guire int ret; 88195fa0405SHaiyang Zhang 88251491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_INIT, 88395fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); 88495fa0405SHaiyang Zhang if (!request) { 88595fa0405SHaiyang Zhang ret = -ENOMEM; 88695fa0405SHaiyang Zhang goto cleanup; 88795fa0405SHaiyang Zhang } 88895fa0405SHaiyang Zhang 88995fa0405SHaiyang Zhang /* Setup the rndis set */ 89095fa0405SHaiyang Zhang init = &request->request_msg.msg.init_req; 89195fa0405SHaiyang Zhang init->major_ver = RNDIS_MAJOR_VERSION; 89295fa0405SHaiyang Zhang init->minor_ver = RNDIS_MINOR_VERSION; 893fb1d074eSHaiyang Zhang init->max_xfer_size = 0x4000; 89495fa0405SHaiyang Zhang 89595fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZING; 89695fa0405SHaiyang Zhang 89795fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 89895fa0405SHaiyang Zhang if (ret != 0) { 89995fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 90095fa0405SHaiyang Zhang goto cleanup; 90195fa0405SHaiyang Zhang } 90295fa0405SHaiyang Zhang 9035362855aSVitaly Kuznetsov wait_for_completion(&request->wait_event); 90495fa0405SHaiyang Zhang 90595fa0405SHaiyang Zhang init_complete = &request->response_msg.msg.init_complete; 90695fa0405SHaiyang Zhang status = init_complete->status; 90795fa0405SHaiyang Zhang if (status == RNDIS_STATUS_SUCCESS) { 90895fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZED; 9097c3877f2SHaiyang Zhang nvdev->max_pkt = init_complete->max_pkt_per_msg; 9107c3877f2SHaiyang Zhang nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor; 91195fa0405SHaiyang Zhang ret = 0; 91295fa0405SHaiyang Zhang } else { 91395fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 91495fa0405SHaiyang Zhang ret = -EINVAL; 91595fa0405SHaiyang Zhang } 91695fa0405SHaiyang Zhang 91795fa0405SHaiyang Zhang cleanup: 91895fa0405SHaiyang Zhang if (request) 91995fa0405SHaiyang Zhang put_rndis_request(dev, request); 92095fa0405SHaiyang Zhang 92195fa0405SHaiyang Zhang return ret; 92295fa0405SHaiyang Zhang } 92395fa0405SHaiyang Zhang 92446b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev) 92546b4f7f5Sstephen hemminger { 92646b4f7f5Sstephen hemminger int i; 92746b4f7f5Sstephen hemminger 92846b4f7f5Sstephen hemminger for (i = 0; i < nvdev->num_chn; i++) { 92946b4f7f5Sstephen hemminger const struct netvsc_channel *nvchan = &nvdev->chan_table[i]; 93046b4f7f5Sstephen hemminger 9317426b1a5Sstephen hemminger if (nvchan->mrc.first != nvchan->mrc.next) 9327426b1a5Sstephen hemminger return false; 9337426b1a5Sstephen hemminger 93446b4f7f5Sstephen hemminger if (atomic_read(&nvchan->queue_sends) > 0) 93546b4f7f5Sstephen hemminger return false; 93646b4f7f5Sstephen hemminger } 93746b4f7f5Sstephen hemminger 93846b4f7f5Sstephen hemminger return true; 93946b4f7f5Sstephen hemminger } 94046b4f7f5Sstephen hemminger 94195fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev) 94295fa0405SHaiyang Zhang { 94395fa0405SHaiyang Zhang struct rndis_request *request; 94495fa0405SHaiyang Zhang struct rndis_halt_request *halt; 9453d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); 9463962981fSstephen hemminger struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); 94795fa0405SHaiyang Zhang 94895fa0405SHaiyang Zhang /* Attempt to do a rndis device halt */ 94951491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_HALT, 95095fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); 95195fa0405SHaiyang Zhang if (!request) 95295fa0405SHaiyang Zhang goto cleanup; 95395fa0405SHaiyang Zhang 95495fa0405SHaiyang Zhang /* Setup the rndis set */ 95595fa0405SHaiyang Zhang halt = &request->request_msg.msg.halt_req; 95695fa0405SHaiyang Zhang halt->req_id = atomic_inc_return(&dev->new_req_id); 95795fa0405SHaiyang Zhang 95895fa0405SHaiyang Zhang /* Ignore return since this msg is optional. */ 95995fa0405SHaiyang Zhang rndis_filter_send_request(dev, request); 96095fa0405SHaiyang Zhang 96195fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 96295fa0405SHaiyang Zhang 96395fa0405SHaiyang Zhang cleanup: 964ae9e63bbSHaiyang Zhang nvdev->destroy = true; 96500ecfb3bSstephen hemminger 96600ecfb3bSstephen hemminger /* Force flag to be ordered before waiting */ 96700ecfb3bSstephen hemminger wmb(); 968ae9e63bbSHaiyang Zhang 969ae9e63bbSHaiyang Zhang /* Wait for all send completions */ 97046b4f7f5Sstephen hemminger wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev)); 971ae9e63bbSHaiyang Zhang 97295fa0405SHaiyang Zhang if (request) 97395fa0405SHaiyang Zhang put_rndis_request(dev, request); 97495fa0405SHaiyang Zhang } 97595fa0405SHaiyang Zhang 97695fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev) 97795fa0405SHaiyang Zhang { 97895fa0405SHaiyang Zhang int ret; 97995fa0405SHaiyang Zhang 98095fa0405SHaiyang Zhang if (dev->state != RNDIS_DEV_INITIALIZED) 98195fa0405SHaiyang Zhang return 0; 98295fa0405SHaiyang Zhang 98395fa0405SHaiyang Zhang ret = rndis_filter_set_packet_filter(dev, 98495fa0405SHaiyang Zhang NDIS_PACKET_TYPE_BROADCAST | 98595fa0405SHaiyang Zhang NDIS_PACKET_TYPE_ALL_MULTICAST | 98695fa0405SHaiyang Zhang NDIS_PACKET_TYPE_DIRECTED); 98795fa0405SHaiyang Zhang if (ret == 0) 98895fa0405SHaiyang Zhang dev->state = RNDIS_DEV_DATAINITIALIZED; 98995fa0405SHaiyang Zhang 99095fa0405SHaiyang Zhang return ret; 99195fa0405SHaiyang Zhang } 99295fa0405SHaiyang Zhang 99395fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev) 99495fa0405SHaiyang Zhang { 99595fa0405SHaiyang Zhang int ret; 99695fa0405SHaiyang Zhang 99795fa0405SHaiyang Zhang if (dev->state != RNDIS_DEV_DATAINITIALIZED) 99895fa0405SHaiyang Zhang return 0; 99995fa0405SHaiyang Zhang 10004f19c0d8Sstephen hemminger /* Make sure rndis_set_multicast doesn't re-enable filter! */ 10014f19c0d8Sstephen hemminger cancel_work_sync(&dev->mcast_work); 10024f19c0d8Sstephen hemminger 100395fa0405SHaiyang Zhang ret = rndis_filter_set_packet_filter(dev, 0); 1004c3582a2cSHaiyang Zhang if (ret == -ENODEV) 1005c3582a2cSHaiyang Zhang ret = 0; 1006c3582a2cSHaiyang Zhang 100795fa0405SHaiyang Zhang if (ret == 0) 100895fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZED; 100995fa0405SHaiyang Zhang 101095fa0405SHaiyang Zhang return ret; 101195fa0405SHaiyang Zhang } 101295fa0405SHaiyang Zhang 10135b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc) 10145b54dac8SHaiyang Zhang { 10153d541ac5SVitaly Kuznetsov struct net_device *ndev = 10163d541ac5SVitaly Kuznetsov hv_get_drvdata(new_sc->primary_channel->device_obj); 1017867047c4Sstephen hemminger struct net_device_context *ndev_ctx = netdev_priv(ndev); 1018867047c4Sstephen hemminger struct netvsc_device *nvscdev; 10195b54dac8SHaiyang Zhang u16 chn_index = new_sc->offermsg.offer.sub_channel_index; 10206de38af6Sstephen hemminger struct netvsc_channel *nvchan; 10216de38af6Sstephen hemminger int ret; 10225b54dac8SHaiyang Zhang 1023867047c4Sstephen hemminger /* This is safe because this callback only happens when 1024867047c4Sstephen hemminger * new device is being setup and waiting on the channel_init_wait. 1025867047c4Sstephen hemminger */ 1026867047c4Sstephen hemminger nvscdev = rcu_dereference_raw(ndev_ctx->nvdev); 1027867047c4Sstephen hemminger if (!nvscdev || chn_index >= nvscdev->num_chn) 10285b54dac8SHaiyang Zhang return; 10295b54dac8SHaiyang Zhang 10306de38af6Sstephen hemminger nvchan = nvscdev->chan_table + chn_index; 10316de38af6Sstephen hemminger 1032b1dd90ceSK. Y. Srinivasan /* Because the device uses NAPI, all the interrupt batching and 1033b1dd90ceSK. Y. Srinivasan * control is done via Net softirq, not the channel handling 1034b1dd90ceSK. Y. Srinivasan */ 1035b1dd90ceSK. Y. Srinivasan set_channel_read_mode(new_sc, HV_CALL_ISR); 1036b1dd90ceSK. Y. Srinivasan 1037bffb1842SK. Y. Srinivasan /* Set the channel before opening.*/ 1038bffb1842SK. Y. Srinivasan nvchan->channel = new_sc; 1039bffb1842SK. Y. Srinivasan 1040a7f99d0fSStephen Hemminger ret = vmbus_open(new_sc, netvsc_ring_bytes, 1041a7f99d0fSStephen Hemminger netvsc_ring_bytes, NULL, 0, 10426de38af6Sstephen hemminger netvsc_channel_cb, nvchan); 104376bb5db5Sstephen hemminger if (ret == 0) 10446de38af6Sstephen hemminger napi_enable(&nvchan->napi); 104576bb5db5Sstephen hemminger else 10468195b139SStephen Hemminger netdev_notice(ndev, "sub channel open failed: %d\n", ret); 104715a863bfSstephen hemminger 10488f2bb1deSStephen Hemminger if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn) 1049732e4985Sstephen hemminger wake_up(&nvscdev->subchan_open); 10505b54dac8SHaiyang Zhang } 10515b54dac8SHaiyang Zhang 10528195b139SStephen Hemminger /* Open sub-channels after completing the handling of the device probe. 10538195b139SStephen Hemminger * This breaks overlap of processing the host message for the 10548195b139SStephen Hemminger * new primary channel with the initialization of sub-channels. 10558195b139SStephen Hemminger */ 10568195b139SStephen Hemminger void rndis_set_subchannel(struct work_struct *w) 10578195b139SStephen Hemminger { 10588195b139SStephen Hemminger struct netvsc_device *nvdev 10598195b139SStephen Hemminger = container_of(w, struct netvsc_device, subchan_work); 10608195b139SStephen Hemminger struct nvsp_message *init_packet = &nvdev->channel_init_pkt; 10618195b139SStephen Hemminger struct net_device_context *ndev_ctx; 10628195b139SStephen Hemminger struct rndis_device *rdev; 10638195b139SStephen Hemminger struct net_device *ndev; 10648195b139SStephen Hemminger struct hv_device *hv_dev; 10658195b139SStephen Hemminger int i, ret; 10668195b139SStephen Hemminger 10678195b139SStephen Hemminger if (!rtnl_trylock()) { 10688195b139SStephen Hemminger schedule_work(w); 10698195b139SStephen Hemminger return; 10708195b139SStephen Hemminger } 10718195b139SStephen Hemminger 10728195b139SStephen Hemminger rdev = nvdev->extension; 10738195b139SStephen Hemminger if (!rdev) 10748195b139SStephen Hemminger goto unlock; /* device was removed */ 10758195b139SStephen Hemminger 10768195b139SStephen Hemminger ndev = rdev->ndev; 10778195b139SStephen Hemminger ndev_ctx = netdev_priv(ndev); 10788195b139SStephen Hemminger hv_dev = ndev_ctx->device_ctx; 10798195b139SStephen Hemminger 10808195b139SStephen Hemminger memset(init_packet, 0, sizeof(struct nvsp_message)); 10818195b139SStephen Hemminger init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL; 10828195b139SStephen Hemminger init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE; 10838195b139SStephen Hemminger init_packet->msg.v5_msg.subchn_req.num_subchannels = 10848195b139SStephen Hemminger nvdev->num_chn - 1; 10858195b139SStephen Hemminger ret = vmbus_sendpacket(hv_dev->channel, init_packet, 10868195b139SStephen Hemminger sizeof(struct nvsp_message), 10878195b139SStephen Hemminger (unsigned long)init_packet, 10888195b139SStephen Hemminger VM_PKT_DATA_INBAND, 10898195b139SStephen Hemminger VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 10908195b139SStephen Hemminger if (ret) { 10918195b139SStephen Hemminger netdev_err(ndev, "sub channel allocate send failed: %d\n", ret); 10928195b139SStephen Hemminger goto failed; 10938195b139SStephen Hemminger } 10948195b139SStephen Hemminger 10958195b139SStephen Hemminger wait_for_completion(&nvdev->channel_init_wait); 10968195b139SStephen Hemminger if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) { 10978195b139SStephen Hemminger netdev_err(ndev, "sub channel request failed\n"); 10988195b139SStephen Hemminger goto failed; 10998195b139SStephen Hemminger } 11008195b139SStephen Hemminger 11018195b139SStephen Hemminger nvdev->num_chn = 1 + 11028195b139SStephen Hemminger init_packet->msg.v5_msg.subchn_comp.num_subchannels; 11038195b139SStephen Hemminger 11048195b139SStephen Hemminger /* wait for all sub channels to open */ 11058195b139SStephen Hemminger wait_event(nvdev->subchan_open, 11068195b139SStephen Hemminger atomic_read(&nvdev->open_chn) == nvdev->num_chn); 11078195b139SStephen Hemminger 11088195b139SStephen Hemminger /* ignore failues from setting rss parameters, still have channels */ 11098195b139SStephen Hemminger rndis_filter_set_rss_param(rdev, netvsc_hash_key); 11108195b139SStephen Hemminger 11118195b139SStephen Hemminger netif_set_real_num_tx_queues(ndev, nvdev->num_chn); 11128195b139SStephen Hemminger netif_set_real_num_rx_queues(ndev, nvdev->num_chn); 11138195b139SStephen Hemminger 1114a6fb6aa3SHaiyang Zhang for (i = 0; i < VRSS_SEND_TAB_SIZE; i++) 1115a6fb6aa3SHaiyang Zhang ndev_ctx->tx_table[i] = i % nvdev->num_chn; 1116a6fb6aa3SHaiyang Zhang 11178195b139SStephen Hemminger rtnl_unlock(); 11188195b139SStephen Hemminger return; 11198195b139SStephen Hemminger 11208195b139SStephen Hemminger failed: 11218195b139SStephen Hemminger /* fallback to only primary channel */ 11228195b139SStephen Hemminger for (i = 1; i < nvdev->num_chn; i++) 11238195b139SStephen Hemminger netif_napi_del(&nvdev->chan_table[i].napi); 11248195b139SStephen Hemminger 11258195b139SStephen Hemminger nvdev->max_chn = 1; 11268195b139SStephen Hemminger nvdev->num_chn = 1; 11278195b139SStephen Hemminger unlock: 11288195b139SStephen Hemminger rtnl_unlock(); 11298195b139SStephen Hemminger } 11308195b139SStephen Hemminger 1131aefd80e8SVitaly Kuznetsov static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device, 1132aefd80e8SVitaly Kuznetsov struct netvsc_device *nvdev) 113395fa0405SHaiyang Zhang { 1134aefd80e8SVitaly Kuznetsov struct net_device *net = rndis_device->ndev; 11353d541ac5SVitaly Kuznetsov struct net_device_context *net_device_ctx = netdev_priv(net); 113623312a3bSstephen hemminger struct ndis_offload hwcaps; 11374a0e70aeSKY Srinivasan struct ndis_offload_params offloads; 113823312a3bSstephen hemminger unsigned int gso_max_size = GSO_MAX_SIZE; 1139aefd80e8SVitaly Kuznetsov int ret; 114095fa0405SHaiyang Zhang 114123312a3bSstephen hemminger /* Find HW offload capabilities */ 1142aefd80e8SVitaly Kuznetsov ret = rndis_query_hwcaps(rndis_device, nvdev, &hwcaps); 11439749fed5Sstephen hemminger if (ret != 0) 1144aefd80e8SVitaly Kuznetsov return ret; 114523312a3bSstephen hemminger 114623312a3bSstephen hemminger /* A value of zero means "no change"; now turn on what we want. */ 11474a0e70aeSKY Srinivasan memset(&offloads, 0, sizeof(struct ndis_offload_params)); 114823312a3bSstephen hemminger 114923312a3bSstephen hemminger /* Linux does not care about IP checksum, always does in kernel */ 115023312a3bSstephen hemminger offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED; 115123312a3bSstephen hemminger 1152aefd80e8SVitaly Kuznetsov /* Reset previously set hw_features flags */ 1153aefd80e8SVitaly Kuznetsov net->hw_features &= ~NETVSC_SUPPORTED_HW_FEATURES; 1154aefd80e8SVitaly Kuznetsov net_device_ctx->tx_checksum_mask = 0; 1155aefd80e8SVitaly Kuznetsov 115623312a3bSstephen hemminger /* Compute tx offload settings based on hw capabilities */ 1157aefd80e8SVitaly Kuznetsov net->hw_features |= NETIF_F_RXCSUM; 115823312a3bSstephen hemminger 115923312a3bSstephen hemminger if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) { 116023312a3bSstephen hemminger /* Can checksum TCP */ 116123312a3bSstephen hemminger net->hw_features |= NETIF_F_IP_CSUM; 116223312a3bSstephen hemminger net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP; 116323312a3bSstephen hemminger 11644a0e70aeSKY Srinivasan offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 116523312a3bSstephen hemminger 116623312a3bSstephen hemminger if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) { 11674a0e70aeSKY Srinivasan offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; 116823312a3bSstephen hemminger net->hw_features |= NETIF_F_TSO; 116923312a3bSstephen hemminger 117023312a3bSstephen hemminger if (hwcaps.lsov2.ip4_maxsz < gso_max_size) 117123312a3bSstephen hemminger gso_max_size = hwcaps.lsov2.ip4_maxsz; 117223312a3bSstephen hemminger } 117323312a3bSstephen hemminger 117423312a3bSstephen hemminger if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) { 117523312a3bSstephen hemminger offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 117623312a3bSstephen hemminger net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP; 117723312a3bSstephen hemminger } 117823312a3bSstephen hemminger } 117923312a3bSstephen hemminger 118023312a3bSstephen hemminger if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) { 118123312a3bSstephen hemminger net->hw_features |= NETIF_F_IPV6_CSUM; 118223312a3bSstephen hemminger 118323312a3bSstephen hemminger offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 118423312a3bSstephen hemminger net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP; 118523312a3bSstephen hemminger 118623312a3bSstephen hemminger if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) && 118723312a3bSstephen hemminger (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) { 118823312a3bSstephen hemminger offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; 118923312a3bSstephen hemminger net->hw_features |= NETIF_F_TSO6; 119023312a3bSstephen hemminger 119123312a3bSstephen hemminger if (hwcaps.lsov2.ip6_maxsz < gso_max_size) 119223312a3bSstephen hemminger gso_max_size = hwcaps.lsov2.ip6_maxsz; 119323312a3bSstephen hemminger } 119423312a3bSstephen hemminger 119523312a3bSstephen hemminger if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) { 119623312a3bSstephen hemminger offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 119723312a3bSstephen hemminger net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP; 119823312a3bSstephen hemminger } 119923312a3bSstephen hemminger } 120023312a3bSstephen hemminger 1201aefd80e8SVitaly Kuznetsov /* In case some hw_features disappeared we need to remove them from 1202aefd80e8SVitaly Kuznetsov * net->features list as they're no longer supported. 1203aefd80e8SVitaly Kuznetsov */ 1204aefd80e8SVitaly Kuznetsov net->features &= ~NETVSC_SUPPORTED_HW_FEATURES | net->hw_features; 1205aefd80e8SVitaly Kuznetsov 120623312a3bSstephen hemminger netif_set_gso_max_size(net, gso_max_size); 12074a0e70aeSKY Srinivasan 1208aefd80e8SVitaly Kuznetsov ret = rndis_filter_set_offload_params(net, nvdev, &offloads); 1209aefd80e8SVitaly Kuznetsov 1210aefd80e8SVitaly Kuznetsov return ret; 1211aefd80e8SVitaly Kuznetsov } 1212aefd80e8SVitaly Kuznetsov 1213aefd80e8SVitaly Kuznetsov struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, 1214aefd80e8SVitaly Kuznetsov struct netvsc_device_info *device_info) 1215aefd80e8SVitaly Kuznetsov { 1216aefd80e8SVitaly Kuznetsov struct net_device *net = hv_get_drvdata(dev); 1217aefd80e8SVitaly Kuznetsov struct netvsc_device *net_device; 1218aefd80e8SVitaly Kuznetsov struct rndis_device *rndis_device; 1219aefd80e8SVitaly Kuznetsov struct ndis_recv_scale_cap rsscap; 1220aefd80e8SVitaly Kuznetsov u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); 1221aefd80e8SVitaly Kuznetsov u32 mtu, size; 1222aefd80e8SVitaly Kuznetsov const struct cpumask *node_cpu_mask; 1223aefd80e8SVitaly Kuznetsov u32 num_possible_rss_qs; 1224aefd80e8SVitaly Kuznetsov int i, ret; 1225aefd80e8SVitaly Kuznetsov 1226aefd80e8SVitaly Kuznetsov rndis_device = get_rndis_device(); 1227aefd80e8SVitaly Kuznetsov if (!rndis_device) 1228aefd80e8SVitaly Kuznetsov return ERR_PTR(-ENODEV); 1229aefd80e8SVitaly Kuznetsov 1230aefd80e8SVitaly Kuznetsov /* Let the inner driver handle this first to create the netvsc channel 1231aefd80e8SVitaly Kuznetsov * NOTE! Once the channel is created, we may get a receive callback 1232aefd80e8SVitaly Kuznetsov * (RndisFilterOnReceive()) before this call is completed 1233aefd80e8SVitaly Kuznetsov */ 1234aefd80e8SVitaly Kuznetsov net_device = netvsc_device_add(dev, device_info); 1235aefd80e8SVitaly Kuznetsov if (IS_ERR(net_device)) { 1236aefd80e8SVitaly Kuznetsov kfree(rndis_device); 1237aefd80e8SVitaly Kuznetsov return net_device; 1238aefd80e8SVitaly Kuznetsov } 1239aefd80e8SVitaly Kuznetsov 1240aefd80e8SVitaly Kuznetsov /* Initialize the rndis device */ 1241aefd80e8SVitaly Kuznetsov net_device->max_chn = 1; 1242aefd80e8SVitaly Kuznetsov net_device->num_chn = 1; 1243aefd80e8SVitaly Kuznetsov 1244aefd80e8SVitaly Kuznetsov net_device->extension = rndis_device; 1245aefd80e8SVitaly Kuznetsov rndis_device->ndev = net; 1246aefd80e8SVitaly Kuznetsov 1247aefd80e8SVitaly Kuznetsov /* Send the rndis initialization message */ 1248aefd80e8SVitaly Kuznetsov ret = rndis_filter_init_device(rndis_device, net_device); 1249aefd80e8SVitaly Kuznetsov if (ret != 0) 1250aefd80e8SVitaly Kuznetsov goto err_dev_remv; 1251aefd80e8SVitaly Kuznetsov 1252aefd80e8SVitaly Kuznetsov /* Get the MTU from the host */ 1253aefd80e8SVitaly Kuznetsov size = sizeof(u32); 1254aefd80e8SVitaly Kuznetsov ret = rndis_filter_query_device(rndis_device, net_device, 1255aefd80e8SVitaly Kuznetsov RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, 1256aefd80e8SVitaly Kuznetsov &mtu, &size); 1257aefd80e8SVitaly Kuznetsov if (ret == 0 && size == sizeof(u32) && mtu < net->mtu) 1258aefd80e8SVitaly Kuznetsov net->mtu = mtu; 1259aefd80e8SVitaly Kuznetsov 1260aefd80e8SVitaly Kuznetsov /* Get the mac address */ 1261aefd80e8SVitaly Kuznetsov ret = rndis_filter_query_device_mac(rndis_device, net_device); 1262aefd80e8SVitaly Kuznetsov if (ret != 0) 1263aefd80e8SVitaly Kuznetsov goto err_dev_remv; 1264aefd80e8SVitaly Kuznetsov 1265aefd80e8SVitaly Kuznetsov memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); 1266aefd80e8SVitaly Kuznetsov 1267aefd80e8SVitaly Kuznetsov /* Query and set hardware capabilities */ 1268aefd80e8SVitaly Kuznetsov ret = rndis_netdev_set_hwcaps(rndis_device, net_device); 1269aefd80e8SVitaly Kuznetsov if (ret != 0) 12704a0e70aeSKY Srinivasan goto err_dev_remv; 12714a0e70aeSKY Srinivasan 1272867047c4Sstephen hemminger rndis_filter_query_device_link_status(rndis_device, net_device); 127395fa0405SHaiyang Zhang 127493ba2222SVitaly Kuznetsov netdev_dbg(net, "Device MAC %pM link state %s\n", 127595fa0405SHaiyang Zhang rndis_device->hw_mac_adr, 1276dedb459eSHaiyang Zhang rndis_device->link_state ? "down" : "up"); 127795fa0405SHaiyang Zhang 12785b54dac8SHaiyang Zhang if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5) 12799749fed5Sstephen hemminger return net_device; 12805b54dac8SHaiyang Zhang 1281867047c4Sstephen hemminger rndis_filter_query_link_speed(rndis_device, net_device); 1282b37879e6SHaiyang Zhang 12835b54dac8SHaiyang Zhang /* vRSS setup */ 12845b54dac8SHaiyang Zhang memset(&rsscap, 0, rsscap_size); 1285867047c4Sstephen hemminger ret = rndis_filter_query_device(rndis_device, net_device, 12865b54dac8SHaiyang Zhang OID_GEN_RECEIVE_SCALE_CAPABILITIES, 12875b54dac8SHaiyang Zhang &rsscap, &rsscap_size); 12885b54dac8SHaiyang Zhang if (ret || rsscap.num_recv_que < 2) 12895b54dac8SHaiyang Zhang goto out; 12905b54dac8SHaiyang Zhang 1291e01ec219SKY Srinivasan /* 1292e01ec219SKY Srinivasan * We will limit the VRSS channels to the number CPUs in the NUMA node 1293e01ec219SKY Srinivasan * the primary channel is currently bound to. 12943071ada4Sstephen hemminger * 12953071ada4Sstephen hemminger * This also guarantees that num_possible_rss_qs <= num_online_cpus 1296e01ec219SKY Srinivasan */ 1297e01ec219SKY Srinivasan node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu)); 12983071ada4Sstephen hemminger num_possible_rss_qs = min_t(u32, cpumask_weight(node_cpu_mask), 12993071ada4Sstephen hemminger rsscap.num_recv_que); 13003071ada4Sstephen hemminger 13013071ada4Sstephen hemminger net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs); 13028ebdcc52SAndrew Schwartzmeyer 13038ebdcc52SAndrew Schwartzmeyer /* We will use the given number of channels if available. */ 13043071ada4Sstephen hemminger net_device->num_chn = min(net_device->max_chn, device_info->num_chn); 1305ff4a4419Sstephen hemminger 1306ff4a4419Sstephen hemminger for (i = 0; i < ITAB_NUM; i++) 130747371300SHaiyang Zhang rndis_device->rx_table[i] = ethtool_rxfh_indir_default( 130847371300SHaiyang Zhang i, net_device->num_chn); 1309ff4a4419Sstephen hemminger 1310732e4985Sstephen hemminger atomic_set(&net_device->open_chn, 1); 13118195b139SStephen Hemminger vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); 13125b54dac8SHaiyang Zhang 13137426b1a5Sstephen hemminger for (i = 1; i < net_device->num_chn; i++) { 13147426b1a5Sstephen hemminger ret = netvsc_alloc_recv_comp_ring(net_device, i); 13157426b1a5Sstephen hemminger if (ret) { 13167426b1a5Sstephen hemminger while (--i != 0) 13177426b1a5Sstephen hemminger vfree(net_device->chan_table[i].mrc.slots); 13187426b1a5Sstephen hemminger goto out; 13197426b1a5Sstephen hemminger } 13207426b1a5Sstephen hemminger } 13217426b1a5Sstephen hemminger 13228195b139SStephen Hemminger for (i = 1; i < net_device->num_chn; i++) 13238195b139SStephen Hemminger netif_napi_add(net, &net_device->chan_table[i].napi, 13248195b139SStephen Hemminger netvsc_poll, NAPI_POLL_WEIGHT); 13255b54dac8SHaiyang Zhang 13268195b139SStephen Hemminger if (net_device->num_chn > 1) 13278195b139SStephen Hemminger schedule_work(&net_device->subchan_work); 13285362855aSVitaly Kuznetsov 13295b54dac8SHaiyang Zhang out: 13308195b139SStephen Hemminger /* if unavailable, just proceed with one queue */ 133159995370SAndrew Schwartzmeyer if (ret) { 133259995370SAndrew Schwartzmeyer net_device->max_chn = 1; 13335b54dac8SHaiyang Zhang net_device->num_chn = 1; 133459995370SAndrew Schwartzmeyer } 1335b3e6b82aSKY Srinivasan 13369749fed5Sstephen hemminger return net_device; 13374a0e70aeSKY Srinivasan 13384a0e70aeSKY Srinivasan err_dev_remv: 13392289f0aaSstephen hemminger rndis_filter_device_remove(dev, net_device); 13409749fed5Sstephen hemminger return ERR_PTR(ret); 134195fa0405SHaiyang Zhang } 134295fa0405SHaiyang Zhang 13432289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev, 13442289f0aaSstephen hemminger struct netvsc_device *net_dev) 134595fa0405SHaiyang Zhang { 134695fa0405SHaiyang Zhang struct rndis_device *rndis_dev = net_dev->extension; 1347d66ab514SHaiyang Zhang 134895fa0405SHaiyang Zhang /* Halt and release the rndis device */ 134995fa0405SHaiyang Zhang rndis_filter_halt_device(rndis_dev); 135095fa0405SHaiyang Zhang 135195fa0405SHaiyang Zhang net_dev->extension = NULL; 135295fa0405SHaiyang Zhang 135395fa0405SHaiyang Zhang netvsc_device_remove(dev); 13548195b139SStephen Hemminger kfree(rndis_dev); 135595fa0405SHaiyang Zhang } 135695fa0405SHaiyang Zhang 13572f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev) 135895fa0405SHaiyang Zhang { 13592f5fa6c8SVitaly Kuznetsov if (!nvdev) 136095fa0405SHaiyang Zhang return -EINVAL; 136195fa0405SHaiyang Zhang 13622f5fa6c8SVitaly Kuznetsov if (atomic_inc_return(&nvdev->open_cnt) != 1) 136384bf9cefSKY Srinivasan return 0; 136484bf9cefSKY Srinivasan 13652f5fa6c8SVitaly Kuznetsov return rndis_filter_open_device(nvdev->extension); 136695fa0405SHaiyang Zhang } 136795fa0405SHaiyang Zhang 13682f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev) 136995fa0405SHaiyang Zhang { 13705fccab3bSHaiyang Zhang if (!nvdev) 137195fa0405SHaiyang Zhang return -EINVAL; 137295fa0405SHaiyang Zhang 137384bf9cefSKY Srinivasan if (atomic_dec_return(&nvdev->open_cnt) != 0) 137484bf9cefSKY Srinivasan return 0; 137584bf9cefSKY Srinivasan 13765fccab3bSHaiyang Zhang return rndis_filter_close_device(nvdev->extension); 137795fa0405SHaiyang Zhang } 1378ea383bf1Sstephen hemminger 1379ea383bf1Sstephen hemminger bool rndis_filter_opened(const struct netvsc_device *nvdev) 1380ea383bf1Sstephen hemminger { 1381ea383bf1Sstephen hemminger return atomic_read(&nvdev->open_cnt) > 0; 1382ea383bf1Sstephen hemminger } 1383