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 
13795fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev,
138dc54a08cSstephen hemminger 			       const struct rndis_message *rndis_msg)
13995fa0405SHaiyang Zhang {
1403d541ac5SVitaly Kuznetsov 	struct net_device *netdev = hv_get_drvdata(hv_dev);
14195fa0405SHaiyang Zhang 
14295fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
14351491167SLinus Walleij 	case RNDIS_MSG_PACKET:
14451491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
14595fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
14695fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14795fa0405SHaiyang Zhang 			   "pkt len %u\n",
14895fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
15095fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
15195fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
15295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
15395fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
15495fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
15595fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
15695fa0405SHaiyang Zhang 		break;
15795fa0405SHaiyang Zhang 
15851491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15951491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
16095fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
16195fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
16295fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
16395fa0405SHaiyang Zhang 			rndis_msg->msg_len,
16495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
16595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
16695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16895fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
17095fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17195fa0405SHaiyang Zhang 			   max_pkt_per_msg,
17295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17395fa0405SHaiyang Zhang 			   pkt_alignment_factor);
17495fa0405SHaiyang Zhang 		break;
17595fa0405SHaiyang Zhang 
17651491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17751491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17895fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17995fa0405SHaiyang Zhang 			"buf offset %u)\n",
18095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
18195fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
18295fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
18395fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18495fa0405SHaiyang Zhang 			   info_buflen,
18595fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18695fa0405SHaiyang Zhang 			   info_buf_offset);
18795fa0405SHaiyang Zhang 		break;
18895fa0405SHaiyang Zhang 
18951491167SLinus Walleij 	case RNDIS_MSG_SET_C:
19095fa0405SHaiyang Zhang 		netdev_dbg(netdev,
19151491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
19295fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19395fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
19495fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
19595fa0405SHaiyang Zhang 		break;
19695fa0405SHaiyang Zhang 
19751491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19851491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19995fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
20095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
20195fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
20295fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
20395fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
20495fa0405SHaiyang Zhang 		break;
20595fa0405SHaiyang Zhang 
20695fa0405SHaiyang Zhang 	default:
20795fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20895fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20995fa0405SHaiyang Zhang 			rndis_msg->msg_len);
21095fa0405SHaiyang Zhang 		break;
21195fa0405SHaiyang Zhang 	}
21295fa0405SHaiyang Zhang }
21395fa0405SHaiyang Zhang 
21495fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
21595fa0405SHaiyang Zhang 				  struct rndis_request *req)
21695fa0405SHaiyang Zhang {
21795fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
218b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
219a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
2203d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
22102b6de01Sstephen hemminger 	int ret;
22295fa0405SHaiyang Zhang 
22395fa0405SHaiyang Zhang 	/* Setup the packet to send it */
22495fa0405SHaiyang Zhang 	packet = &req->pkt;
22595fa0405SHaiyang Zhang 
22695fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
22795fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
22895fa0405SHaiyang Zhang 
229a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
23095fa0405SHaiyang Zhang 					PAGE_SHIFT;
231a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
232a9f2e2d6SKY Srinivasan 	pb[0].offset =
23395fa0405SHaiyang Zhang 		(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
23495fa0405SHaiyang Zhang 
23599e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
236a9f2e2d6SKY Srinivasan 	if (pb[0].offset + pb[0].len > PAGE_SIZE) {
23799e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
238a9f2e2d6SKY Srinivasan 		pb[0].len = PAGE_SIZE -
239a9f2e2d6SKY Srinivasan 			pb[0].offset;
240a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
241a9f2e2d6SKY Srinivasan 			+ pb[0].len) >> PAGE_SHIFT;
242a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
243a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
244a9f2e2d6SKY Srinivasan 			pb[0].len;
24599e3fcfaSHaiyang Zhang 	}
24699e3fcfaSHaiyang Zhang 
247867047c4Sstephen hemminger 	rcu_read_lock_bh();
24802b6de01Sstephen hemminger 	ret = netvsc_send(net_device_ctx, packet, NULL, pb, NULL);
249867047c4Sstephen hemminger 	rcu_read_unlock_bh();
250867047c4Sstephen hemminger 
25195fa0405SHaiyang Zhang 	return ret;
25295fa0405SHaiyang Zhang }
25395fa0405SHaiyang Zhang 
2541b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2551b07da51SHaiyang Zhang 				 struct rndis_request *request)
2561b07da51SHaiyang Zhang {
2571b07da51SHaiyang Zhang 	u32 link_status;
2581b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2591b07da51SHaiyang Zhang 
2601b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2611b07da51SHaiyang Zhang 
2621b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2631b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2641b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2651b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2661b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2671b07da51SHaiyang Zhang 	}
2681b07da51SHaiyang Zhang }
2691b07da51SHaiyang Zhang 
27095fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev,
27195fa0405SHaiyang Zhang 				       struct rndis_message *resp)
27295fa0405SHaiyang Zhang {
27395fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
27495fa0405SHaiyang Zhang 	bool found = false;
27595fa0405SHaiyang Zhang 	unsigned long flags;
2763d541ac5SVitaly Kuznetsov 	struct net_device *ndev = dev->ndev;
27795fa0405SHaiyang Zhang 
27895fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
27995fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
28095fa0405SHaiyang Zhang 		/*
28195fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
28295fa0405SHaiyang Zhang 		 * field
28395fa0405SHaiyang Zhang 		 */
28495fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
28595fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
28695fa0405SHaiyang Zhang 			found = true;
28795fa0405SHaiyang Zhang 			break;
28895fa0405SHaiyang Zhang 		}
28995fa0405SHaiyang Zhang 	}
29095fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
29195fa0405SHaiyang Zhang 
29295fa0405SHaiyang Zhang 	if (found) {
293a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
294a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
29595fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
29695fa0405SHaiyang Zhang 			       resp->msg_len);
2971b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
2981b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
2991b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
3001b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
30195fa0405SHaiyang Zhang 		} else {
30295fa0405SHaiyang Zhang 			netdev_err(ndev,
30395fa0405SHaiyang Zhang 				"rndis response buffer overflow "
30495fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
30595fa0405SHaiyang Zhang 				resp->msg_len,
30686eedaccSKY Srinivasan 				sizeof(struct rndis_message));
30795fa0405SHaiyang Zhang 
30895fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
30951491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
31095fa0405SHaiyang Zhang 				/* does not have a request id field */
31195fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
312007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
31395fa0405SHaiyang Zhang 			} else {
31495fa0405SHaiyang Zhang 				request->response_msg.msg.
31595fa0405SHaiyang Zhang 				init_complete.status =
316007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
31795fa0405SHaiyang Zhang 			}
31895fa0405SHaiyang Zhang 		}
31995fa0405SHaiyang Zhang 
32095fa0405SHaiyang Zhang 		complete(&request->wait_event);
32195fa0405SHaiyang Zhang 	} else {
32295fa0405SHaiyang Zhang 		netdev_err(ndev,
32395fa0405SHaiyang Zhang 			"no rndis request found for this response "
32495fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
32595fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
32695fa0405SHaiyang Zhang 			resp->ndis_msg_type);
32795fa0405SHaiyang Zhang 	}
32895fa0405SHaiyang Zhang }
32995fa0405SHaiyang Zhang 
3301f5f3a75SHaiyang Zhang /*
3311f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3321f5f3a75SHaiyang Zhang  * return NULL if not found.
3331f5f3a75SHaiyang Zhang  */
3341f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3351f5f3a75SHaiyang Zhang {
3361f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3371f5f3a75SHaiyang Zhang 	int len;
3381f5f3a75SHaiyang Zhang 
3391f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3401f5f3a75SHaiyang Zhang 		return NULL;
3411f5f3a75SHaiyang Zhang 
3421f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3431f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3441f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3451f5f3a75SHaiyang Zhang 
3461f5f3a75SHaiyang Zhang 	while (len > 0) {
3471f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3481f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3491f5f3a75SHaiyang Zhang 		len -= ppi->size;
3501f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3511f5f3a75SHaiyang Zhang 	}
3521f5f3a75SHaiyang Zhang 
3531f5f3a75SHaiyang Zhang 	return NULL;
3541f5f3a75SHaiyang Zhang }
3551f5f3a75SHaiyang Zhang 
356dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
357dc54a08cSstephen hemminger 				     struct rndis_device *dev,
35895fa0405SHaiyang Zhang 				     struct rndis_message *msg,
359dc54a08cSstephen hemminger 				     struct vmbus_channel *channel,
360dc54a08cSstephen hemminger 				     void *data, u32 data_buflen)
36195fa0405SHaiyang Zhang {
362dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
363dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
364dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
36595fa0405SHaiyang Zhang 	u32 data_offset;
36695fa0405SHaiyang Zhang 
36795fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
36895fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
36995fa0405SHaiyang Zhang 
370dc54a08cSstephen hemminger 	data_buflen -= data_offset;
3714b8a8bc9SWei Yongjun 
3724b8a8bc9SWei Yongjun 	/*
3734b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3744b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3754b8a8bc9SWei Yongjun 	 */
376dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
3773d541ac5SVitaly Kuznetsov 		netdev_err(dev->ndev, "rndis message buffer "
3784b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3794b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
380dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
38110082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
3824b8a8bc9SWei Yongjun 	}
3834b8a8bc9SWei Yongjun 
384dc54a08cSstephen hemminger 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
385dc54a08cSstephen hemminger 
3864b8a8bc9SWei Yongjun 	/*
3874b8a8bc9SWei Yongjun 	 * Remove the rndis trailer padding from rndis packet message
3884b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
3894b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
3904b8a8bc9SWei Yongjun 	 */
391dc54a08cSstephen hemminger 	data = (void *)((unsigned long)data + data_offset);
392e3d605edSKY Srinivasan 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
393dc54a08cSstephen hemminger 	return netvsc_recv_callback(ndev, channel,
394dc54a08cSstephen hemminger 				    data, rndis_pkt->data_len,
395dc54a08cSstephen hemminger 				    csum_info, vlan);
39695fa0405SHaiyang Zhang }
39795fa0405SHaiyang Zhang 
398dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
399dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
400dc54a08cSstephen hemminger 			 struct hv_device *dev,
401dc54a08cSstephen hemminger 			 struct vmbus_channel *channel,
402dc54a08cSstephen hemminger 			 void *data, u32 buflen)
40395fa0405SHaiyang Zhang {
4043d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
405dc54a08cSstephen hemminger 	struct rndis_device *rndis_dev = net_dev->extension;
406dc54a08cSstephen hemminger 	struct rndis_message *rndis_msg = data;
40795fa0405SHaiyang Zhang 
40895fa0405SHaiyang Zhang 	/* Make sure the rndis device state is initialized */
409dc54a08cSstephen hemminger 	if (unlikely(!rndis_dev)) {
410dc54a08cSstephen hemminger 		netif_err(net_device_ctx, rx_err, ndev,
411dc54a08cSstephen hemminger 			  "got rndis message but no rndis device!\n");
412dc54a08cSstephen hemminger 		return NVSP_STAT_FAIL;
41395fa0405SHaiyang Zhang 	}
41495fa0405SHaiyang Zhang 
415dc54a08cSstephen hemminger 	if (unlikely(rndis_dev->state == RNDIS_DEV_UNINITIALIZED)) {
416dc54a08cSstephen hemminger 		netif_err(net_device_ctx, rx_err, ndev,
417dc54a08cSstephen hemminger 			  "got rndis message uninitialized\n");
418dc54a08cSstephen hemminger 		return NVSP_STAT_FAIL;
41995fa0405SHaiyang Zhang 	}
42095fa0405SHaiyang Zhang 
421dc54a08cSstephen hemminger 	if (netif_msg_rx_status(net_device_ctx))
422ef31bef6SHaiyang Zhang 		dump_rndis_message(dev, rndis_msg);
42395fa0405SHaiyang Zhang 
424ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
42551491167SLinus Walleij 	case RNDIS_MSG_PACKET:
426dc54a08cSstephen hemminger 		return rndis_filter_receive_data(ndev, rndis_dev, rndis_msg,
427dc54a08cSstephen hemminger 						 channel, data, buflen);
42851491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
42951491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
43051491167SLinus Walleij 	case RNDIS_MSG_SET_C:
43195fa0405SHaiyang Zhang 		/* completion msgs */
432ef31bef6SHaiyang Zhang 		rndis_filter_receive_response(rndis_dev, rndis_msg);
43395fa0405SHaiyang Zhang 		break;
43495fa0405SHaiyang Zhang 
43551491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
43695fa0405SHaiyang Zhang 		/* notification msgs */
4373a494e71SHaiyang Zhang 		netvsc_linkstatus_callback(dev, rndis_msg);
43895fa0405SHaiyang Zhang 		break;
43995fa0405SHaiyang Zhang 	default:
44095fa0405SHaiyang Zhang 		netdev_err(ndev,
44195fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
442ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
443ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
44495fa0405SHaiyang Zhang 		break;
44595fa0405SHaiyang Zhang 	}
44695fa0405SHaiyang Zhang 
447dc54a08cSstephen hemminger 	return 0;
44895fa0405SHaiyang Zhang }
44995fa0405SHaiyang Zhang 
450867047c4Sstephen hemminger static int rndis_filter_query_device(struct rndis_device *dev,
451867047c4Sstephen hemminger 				     struct netvsc_device *nvdev,
452867047c4Sstephen hemminger 				     u32 oid, void *result, u32 *result_size)
45395fa0405SHaiyang Zhang {
45495fa0405SHaiyang Zhang 	struct rndis_request *request;
45595fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
45695fa0405SHaiyang Zhang 	struct rndis_query_request *query;
45795fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
45895fa0405SHaiyang Zhang 	int ret = 0;
45995fa0405SHaiyang Zhang 
46095fa0405SHaiyang Zhang 	if (!result)
46195fa0405SHaiyang Zhang 		return -EINVAL;
46295fa0405SHaiyang Zhang 
46395fa0405SHaiyang Zhang 	*result_size = 0;
46451491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
46595fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
46695fa0405SHaiyang Zhang 	if (!request) {
46795fa0405SHaiyang Zhang 		ret = -ENOMEM;
46895fa0405SHaiyang Zhang 		goto cleanup;
46995fa0405SHaiyang Zhang 	}
47095fa0405SHaiyang Zhang 
47195fa0405SHaiyang Zhang 	/* Setup the rndis query */
47295fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
47395fa0405SHaiyang Zhang 	query->oid = oid;
47495fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
47595fa0405SHaiyang Zhang 	query->info_buflen = 0;
47695fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
47795fa0405SHaiyang Zhang 
47823312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
47923312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
48023312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
48123312a3bSstephen hemminger 		u8 ndis_rev;
48223312a3bSstephen hemminger 		size_t size;
48323312a3bSstephen hemminger 
48423312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
48523312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
48623312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
48723312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
48823312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
48923312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
49023312a3bSstephen hemminger 		} else {
49123312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
49223312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
49323312a3bSstephen hemminger 		}
49423312a3bSstephen hemminger 
49523312a3bSstephen hemminger 		request->request_msg.msg_len += size;
49623312a3bSstephen hemminger 		query->info_buflen = size;
49723312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
49823312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
49923312a3bSstephen hemminger 
50023312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
50123312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
50223312a3bSstephen hemminger 		hwcaps->header.size = size;
50323312a3bSstephen hemminger 
50423312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
5055b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
5065b54dac8SHaiyang Zhang 
5075b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
5085b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
5095b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5105b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5115b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5125b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5135b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5145b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5155b54dac8SHaiyang Zhang 	}
5165b54dac8SHaiyang Zhang 
51795fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
51895fa0405SHaiyang Zhang 	if (ret != 0)
51995fa0405SHaiyang Zhang 		goto cleanup;
52095fa0405SHaiyang Zhang 
5215362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
52295fa0405SHaiyang Zhang 
52395fa0405SHaiyang Zhang 	/* Copy the response back */
52495fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
52595fa0405SHaiyang Zhang 
52695fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
52795fa0405SHaiyang Zhang 		ret = -1;
52895fa0405SHaiyang Zhang 		goto cleanup;
52995fa0405SHaiyang Zhang 	}
53095fa0405SHaiyang Zhang 
53195fa0405SHaiyang Zhang 	memcpy(result,
53295fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
53395fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
53495fa0405SHaiyang Zhang 	       query_complete->info_buflen);
53595fa0405SHaiyang Zhang 
53695fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
53795fa0405SHaiyang Zhang 
53895fa0405SHaiyang Zhang cleanup:
53995fa0405SHaiyang Zhang 	if (request)
54095fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
54195fa0405SHaiyang Zhang 
54295fa0405SHaiyang Zhang 	return ret;
54395fa0405SHaiyang Zhang }
54495fa0405SHaiyang Zhang 
54523312a3bSstephen hemminger /* Get the hardware offload capabilities */
54623312a3bSstephen hemminger static int
547867047c4Sstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device,
548867047c4Sstephen hemminger 		   struct ndis_offload *caps)
54923312a3bSstephen hemminger {
55023312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
55123312a3bSstephen hemminger 	int ret;
55223312a3bSstephen hemminger 
55323312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
55423312a3bSstephen hemminger 
555867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
55623312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
55723312a3bSstephen hemminger 					caps, &caps_len);
55823312a3bSstephen hemminger 	if (ret)
55923312a3bSstephen hemminger 		return ret;
56023312a3bSstephen hemminger 
56123312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
56223312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
56323312a3bSstephen hemminger 			    caps->header.type);
56423312a3bSstephen hemminger 		return -EINVAL;
56523312a3bSstephen hemminger 	}
56623312a3bSstephen hemminger 
56723312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
56823312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
56923312a3bSstephen hemminger 			    caps->header.revision);
57023312a3bSstephen hemminger 		return -EINVAL;
57123312a3bSstephen hemminger 	}
57223312a3bSstephen hemminger 
57323312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
57423312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
57523312a3bSstephen hemminger 		netdev_warn(dev->ndev,
57623312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
57723312a3bSstephen hemminger 			    caps->header.size, caps_len);
57823312a3bSstephen hemminger 		return -EINVAL;
57923312a3bSstephen hemminger 	}
58023312a3bSstephen hemminger 
58123312a3bSstephen hemminger 	return 0;
58223312a3bSstephen hemminger }
58323312a3bSstephen hemminger 
584867047c4Sstephen hemminger static int rndis_filter_query_device_mac(struct rndis_device *dev,
585867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
58695fa0405SHaiyang Zhang {
58795fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
58895fa0405SHaiyang Zhang 
589867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
59095fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
59195fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
59295fa0405SHaiyang Zhang }
59395fa0405SHaiyang Zhang 
5941ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
5951ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
5961ce09e89SHaiyang Zhang 
597867047c4Sstephen hemminger int rndis_filter_set_device_mac(struct netvsc_device *nvdev,
598867047c4Sstephen hemminger 				const char *mac)
5991ce09e89SHaiyang Zhang {
6001ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
6011ce09e89SHaiyang Zhang 	struct rndis_request *request;
6021ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
6031ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
6041ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
6051ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
6061ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
6071ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
6081ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
609999028ccSNicholas Mc Guire 	int ret;
6101ce09e89SHaiyang Zhang 
6111ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6121ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6131ce09e89SHaiyang Zhang 	if (!request)
6141ce09e89SHaiyang Zhang 		return -ENOMEM;
6151ce09e89SHaiyang Zhang 
6161ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
6171ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
6181ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
6191ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
6201ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
6211ce09e89SHaiyang Zhang 
6221ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
6231ce09e89SHaiyang Zhang 		set->info_buf_offset);
6241ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
6251ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
6261ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
6271ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
6281ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
6291ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
6301ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
6311ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
6321ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
6331ce09e89SHaiyang Zhang 
6341ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
6351ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
6361ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
6371ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
6381ce09e89SHaiyang Zhang 	if (ret < 0)
6391ce09e89SHaiyang Zhang 		goto cleanup;
6401ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
6411ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
6421ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
6431ce09e89SHaiyang Zhang 	if (ret < 0)
6441ce09e89SHaiyang Zhang 		goto cleanup;
6451ce09e89SHaiyang Zhang 
6461ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6471ce09e89SHaiyang Zhang 	if (ret != 0)
6481ce09e89SHaiyang Zhang 		goto cleanup;
6491ce09e89SHaiyang Zhang 
6505362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
6515362855aSVitaly Kuznetsov 
6521ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
653867047c4Sstephen hemminger 	if (set_complete->status != RNDIS_STATUS_SUCCESS)
654867047c4Sstephen hemminger 		ret = -EIO;
6551ce09e89SHaiyang Zhang 
6561ce09e89SHaiyang Zhang cleanup:
6571ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6581ce09e89SHaiyang Zhang 	return ret;
6591ce09e89SHaiyang Zhang }
6601ce09e89SHaiyang Zhang 
661da19fcd0SLad, Prabhakar static int
662426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
6639749fed5Sstephen hemminger 				struct netvsc_device *nvdev,
6644a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6654a0e70aeSKY Srinivasan {
6664a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6674a0e70aeSKY Srinivasan 	struct rndis_request *request;
6684a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6694a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6704a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6714a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
672999028ccSNicholas Mc Guire 	int ret;
673af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
674af9893a3SKY Srinivasan 
675af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
676af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
677af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
678af9893a3SKY Srinivasan 		 * UDP checksum offload.
679af9893a3SKY Srinivasan 		 */
680af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
681af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
682af9893a3SKY Srinivasan 	}
6834a0e70aeSKY Srinivasan 
6844a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6854a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6864a0e70aeSKY Srinivasan 	if (!request)
6874a0e70aeSKY Srinivasan 		return -ENOMEM;
6884a0e70aeSKY Srinivasan 
6894a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
6904a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
6914a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
6924a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
6934a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
6944a0e70aeSKY Srinivasan 
6954a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
6964a0e70aeSKY Srinivasan 				set->info_buf_offset);
6974a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
6984a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
6994a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
7004a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
7014a0e70aeSKY Srinivasan 
7024a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
7034a0e70aeSKY Srinivasan 	if (ret != 0)
7044a0e70aeSKY Srinivasan 		goto cleanup;
7054a0e70aeSKY Srinivasan 
7065362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7074a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
7084a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
709af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
7104a0e70aeSKY Srinivasan 			   set_complete->status);
7114a0e70aeSKY Srinivasan 		ret = -EINVAL;
7124a0e70aeSKY Srinivasan 	}
7134a0e70aeSKY Srinivasan 
7144a0e70aeSKY Srinivasan cleanup:
7154a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
7164a0e70aeSKY Srinivasan 	return ret;
7174a0e70aeSKY Srinivasan }
7181ce09e89SHaiyang Zhang 
719962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev,
720715e2ec5SHaiyang Zhang 			       const u8 *rss_key)
7215b54dac8SHaiyang Zhang {
7223d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
7235b54dac8SHaiyang Zhang 	struct rndis_request *request;
7245b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7255b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7265b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
727962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
7285b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7295b54dac8SHaiyang Zhang 	u32 *itab;
7305b54dac8SHaiyang Zhang 	u8 *keyp;
731999028ccSNicholas Mc Guire 	int i, ret;
7325b54dac8SHaiyang Zhang 
7335b54dac8SHaiyang Zhang 	request = get_rndis_request(
7345b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7355b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7365b54dac8SHaiyang Zhang 	if (!request)
7375b54dac8SHaiyang Zhang 		return -ENOMEM;
7385b54dac8SHaiyang Zhang 
7395b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7405b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7415b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7425b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7435b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7445b54dac8SHaiyang Zhang 
7455b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7465b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7475b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7485b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7495b54dac8SHaiyang Zhang 	rssp->flag = 0;
7505b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7514c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
7524c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
7535b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7545b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
755962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
7565b54dac8SHaiyang Zhang 	rssp->kashkey_offset = rssp->indirect_taboffset +
7575b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7585b54dac8SHaiyang Zhang 
7595b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7605b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7615b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
76247371300SHaiyang Zhang 		itab[i] = rdev->rx_table[i];
7635b54dac8SHaiyang Zhang 
7645b54dac8SHaiyang Zhang 	/* Set hask key values */
7655b54dac8SHaiyang Zhang 	keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
766962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
7675b54dac8SHaiyang Zhang 
7685b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7695b54dac8SHaiyang Zhang 	if (ret != 0)
7705b54dac8SHaiyang Zhang 		goto cleanup;
7715b54dac8SHaiyang Zhang 
7725362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7735b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
774962f3feeSstephen hemminger 	if (set_complete->status == RNDIS_STATUS_SUCCESS)
775962f3feeSstephen hemminger 		memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
776962f3feeSstephen hemminger 	else {
7775b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7785b54dac8SHaiyang Zhang 			   set_complete->status);
7795b54dac8SHaiyang Zhang 		ret = -EINVAL;
7805b54dac8SHaiyang Zhang 	}
7815b54dac8SHaiyang Zhang 
7825b54dac8SHaiyang Zhang cleanup:
7835b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
7845b54dac8SHaiyang Zhang 	return ret;
7855b54dac8SHaiyang Zhang }
7865b54dac8SHaiyang Zhang 
787867047c4Sstephen hemminger static int rndis_filter_query_device_link_status(struct rndis_device *dev,
788867047c4Sstephen hemminger 						 struct netvsc_device *net_device)
78995fa0405SHaiyang Zhang {
79095fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
79195fa0405SHaiyang Zhang 	u32 link_status;
79295fa0405SHaiyang Zhang 
793867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
79495fa0405SHaiyang Zhang 					 RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
79595fa0405SHaiyang Zhang 					 &link_status, &size);
79695fa0405SHaiyang Zhang }
79795fa0405SHaiyang Zhang 
798867047c4Sstephen hemminger static int rndis_filter_query_link_speed(struct rndis_device *dev,
799867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
800b37879e6SHaiyang Zhang {
801b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
802b37879e6SHaiyang Zhang 	u32 link_speed;
803b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
804b37879e6SHaiyang Zhang 	int ret;
805b37879e6SHaiyang Zhang 
806867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
807867047c4Sstephen hemminger 					RNDIS_OID_GEN_LINK_SPEED,
808b37879e6SHaiyang Zhang 					&link_speed, &size);
809b37879e6SHaiyang Zhang 
810b37879e6SHaiyang Zhang 	if (!ret) {
811b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
812b37879e6SHaiyang Zhang 
813b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
814b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
815b37879e6SHaiyang Zhang 		 */
816b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
817b37879e6SHaiyang Zhang 	}
818b37879e6SHaiyang Zhang 
819b37879e6SHaiyang Zhang 	return ret;
820b37879e6SHaiyang Zhang }
821b37879e6SHaiyang Zhang 
8224f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev,
8234f19c0d8Sstephen hemminger 					  u32 new_filter)
82495fa0405SHaiyang Zhang {
82595fa0405SHaiyang Zhang 	struct rndis_request *request;
82695fa0405SHaiyang Zhang 	struct rndis_set_request *set;
827999028ccSNicholas Mc Guire 	int ret;
82895fa0405SHaiyang Zhang 
82951491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
83095fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
83195fa0405SHaiyang Zhang 			sizeof(u32));
832ce12b810Sstephen hemminger 	if (!request)
833ce12b810Sstephen hemminger 		return -ENOMEM;
834ce12b810Sstephen hemminger 
83595fa0405SHaiyang Zhang 
83695fa0405SHaiyang Zhang 	/* Setup the rndis set */
83795fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
83895fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
83995fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
84095fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
84195fa0405SHaiyang Zhang 
84295fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
84395fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
84495fa0405SHaiyang Zhang 
84595fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
846ce12b810Sstephen hemminger 	if (ret == 0)
8475362855aSVitaly Kuznetsov 		wait_for_completion(&request->wait_event);
84895fa0405SHaiyang Zhang 
84995fa0405SHaiyang Zhang 	put_rndis_request(dev, request);
850ce12b810Sstephen hemminger 
85195fa0405SHaiyang Zhang 	return ret;
85295fa0405SHaiyang Zhang }
85395fa0405SHaiyang Zhang 
8544f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w)
8554f19c0d8Sstephen hemminger {
8564f19c0d8Sstephen hemminger 	struct rndis_device *rdev
8574f19c0d8Sstephen hemminger 		= container_of(w, struct rndis_device, mcast_work);
8584f19c0d8Sstephen hemminger 
8594f19c0d8Sstephen hemminger 	if (rdev->ndev->flags & IFF_PROMISC)
8604f19c0d8Sstephen hemminger 		rndis_filter_set_packet_filter(rdev,
8614f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_PROMISCUOUS);
8624f19c0d8Sstephen hemminger 	else
8634f19c0d8Sstephen hemminger 		rndis_filter_set_packet_filter(rdev,
8644f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_BROADCAST |
8654f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_ALL_MULTICAST |
8664f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_DIRECTED);
8674f19c0d8Sstephen hemminger }
8684f19c0d8Sstephen hemminger 
8694f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev)
8704f19c0d8Sstephen hemminger {
8714f19c0d8Sstephen hemminger 	struct rndis_device *rdev = nvdev->extension;
8724f19c0d8Sstephen hemminger 
8734f19c0d8Sstephen hemminger 	schedule_work(&rdev->mcast_work);
8744f19c0d8Sstephen hemminger }
8754f19c0d8Sstephen hemminger 
876867047c4Sstephen hemminger static int rndis_filter_init_device(struct rndis_device *dev,
877867047c4Sstephen hemminger 				    struct netvsc_device *nvdev)
87895fa0405SHaiyang Zhang {
87995fa0405SHaiyang Zhang 	struct rndis_request *request;
88095fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
88195fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
88295fa0405SHaiyang Zhang 	u32 status;
883999028ccSNicholas Mc Guire 	int ret;
88495fa0405SHaiyang Zhang 
88551491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
88695fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
88795fa0405SHaiyang Zhang 	if (!request) {
88895fa0405SHaiyang Zhang 		ret = -ENOMEM;
88995fa0405SHaiyang Zhang 		goto cleanup;
89095fa0405SHaiyang Zhang 	}
89195fa0405SHaiyang Zhang 
89295fa0405SHaiyang Zhang 	/* Setup the rndis set */
89395fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
89495fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
89595fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
896fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
89795fa0405SHaiyang Zhang 
89895fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
89995fa0405SHaiyang Zhang 
90095fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
90195fa0405SHaiyang Zhang 	if (ret != 0) {
90295fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
90395fa0405SHaiyang Zhang 		goto cleanup;
90495fa0405SHaiyang Zhang 	}
90595fa0405SHaiyang Zhang 
9065362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
90795fa0405SHaiyang Zhang 
90895fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
90995fa0405SHaiyang Zhang 	status = init_complete->status;
91095fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
91195fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
9127c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
9137c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
91495fa0405SHaiyang Zhang 		ret = 0;
91595fa0405SHaiyang Zhang 	} else {
91695fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
91795fa0405SHaiyang Zhang 		ret = -EINVAL;
91895fa0405SHaiyang Zhang 	}
91995fa0405SHaiyang Zhang 
92095fa0405SHaiyang Zhang cleanup:
92195fa0405SHaiyang Zhang 	if (request)
92295fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
92395fa0405SHaiyang Zhang 
92495fa0405SHaiyang Zhang 	return ret;
92595fa0405SHaiyang Zhang }
92695fa0405SHaiyang Zhang 
92746b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev)
92846b4f7f5Sstephen hemminger {
92946b4f7f5Sstephen hemminger 	int i;
93046b4f7f5Sstephen hemminger 
93146b4f7f5Sstephen hemminger 	for (i = 0; i < nvdev->num_chn; i++) {
93246b4f7f5Sstephen hemminger 		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
93346b4f7f5Sstephen hemminger 
9347426b1a5Sstephen hemminger 		if (nvchan->mrc.first != nvchan->mrc.next)
9357426b1a5Sstephen hemminger 			return false;
9367426b1a5Sstephen hemminger 
93746b4f7f5Sstephen hemminger 		if (atomic_read(&nvchan->queue_sends) > 0)
93846b4f7f5Sstephen hemminger 			return false;
93946b4f7f5Sstephen hemminger 	}
94046b4f7f5Sstephen hemminger 
94146b4f7f5Sstephen hemminger 	return true;
94246b4f7f5Sstephen hemminger }
94346b4f7f5Sstephen hemminger 
94495fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev)
94595fa0405SHaiyang Zhang {
94695fa0405SHaiyang Zhang 	struct rndis_request *request;
94795fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
9483d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
9493962981fSstephen hemminger 	struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
95095fa0405SHaiyang Zhang 
95195fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
95251491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
95395fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
95495fa0405SHaiyang Zhang 	if (!request)
95595fa0405SHaiyang Zhang 		goto cleanup;
95695fa0405SHaiyang Zhang 
95795fa0405SHaiyang Zhang 	/* Setup the rndis set */
95895fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
95995fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
96095fa0405SHaiyang Zhang 
96195fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
96295fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
96395fa0405SHaiyang Zhang 
96495fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
96595fa0405SHaiyang Zhang 
96695fa0405SHaiyang Zhang cleanup:
967ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
96800ecfb3bSstephen hemminger 
96900ecfb3bSstephen hemminger 	/* Force flag to be ordered before waiting */
97000ecfb3bSstephen hemminger 	wmb();
971ae9e63bbSHaiyang Zhang 
972ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
97346b4f7f5Sstephen hemminger 	wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
974ae9e63bbSHaiyang Zhang 
97595fa0405SHaiyang Zhang 	if (request)
97695fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
97795fa0405SHaiyang Zhang }
97895fa0405SHaiyang Zhang 
97995fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
98095fa0405SHaiyang Zhang {
98195fa0405SHaiyang Zhang 	int ret;
98295fa0405SHaiyang Zhang 
98395fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
98495fa0405SHaiyang Zhang 		return 0;
98595fa0405SHaiyang Zhang 
98695fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
98795fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
98895fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
98995fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
99095fa0405SHaiyang Zhang 	if (ret == 0)
99195fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
99295fa0405SHaiyang Zhang 
99395fa0405SHaiyang Zhang 	return ret;
99495fa0405SHaiyang Zhang }
99595fa0405SHaiyang Zhang 
99695fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
99795fa0405SHaiyang Zhang {
99895fa0405SHaiyang Zhang 	int ret;
99995fa0405SHaiyang Zhang 
100095fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
100195fa0405SHaiyang Zhang 		return 0;
100295fa0405SHaiyang Zhang 
10034f19c0d8Sstephen hemminger 	/* Make sure rndis_set_multicast doesn't re-enable filter! */
10044f19c0d8Sstephen hemminger 	cancel_work_sync(&dev->mcast_work);
10054f19c0d8Sstephen hemminger 
100695fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
1007c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
1008c3582a2cSHaiyang Zhang 		ret = 0;
1009c3582a2cSHaiyang Zhang 
101095fa0405SHaiyang Zhang 	if (ret == 0)
101195fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
101295fa0405SHaiyang Zhang 
101395fa0405SHaiyang Zhang 	return ret;
101495fa0405SHaiyang Zhang }
101595fa0405SHaiyang Zhang 
10165b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
10175b54dac8SHaiyang Zhang {
10183d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
10193d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
1020867047c4Sstephen hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
1021867047c4Sstephen hemminger 	struct netvsc_device *nvscdev;
10225b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
10236de38af6Sstephen hemminger 	struct netvsc_channel *nvchan;
10246de38af6Sstephen hemminger 	int ret;
10255b54dac8SHaiyang Zhang 
1026867047c4Sstephen hemminger 	/* This is safe because this callback only happens when
1027867047c4Sstephen hemminger 	 * new device is being setup and waiting on the channel_init_wait.
1028867047c4Sstephen hemminger 	 */
1029867047c4Sstephen hemminger 	nvscdev = rcu_dereference_raw(ndev_ctx->nvdev);
1030867047c4Sstephen hemminger 	if (!nvscdev || chn_index >= nvscdev->num_chn)
10315b54dac8SHaiyang Zhang 		return;
10325b54dac8SHaiyang Zhang 
10336de38af6Sstephen hemminger 	nvchan = nvscdev->chan_table + chn_index;
10346de38af6Sstephen hemminger 
1035b1dd90ceSK. Y. Srinivasan 	/* Because the device uses NAPI, all the interrupt batching and
1036b1dd90ceSK. Y. Srinivasan 	 * control is done via Net softirq, not the channel handling
1037b1dd90ceSK. Y. Srinivasan 	 */
1038b1dd90ceSK. Y. Srinivasan 	set_channel_read_mode(new_sc, HV_CALL_ISR);
1039b1dd90ceSK. Y. Srinivasan 
1040bffb1842SK. Y. Srinivasan 	/* Set the channel before opening.*/
1041bffb1842SK. Y. Srinivasan 	nvchan->channel = new_sc;
1042bffb1842SK. Y. Srinivasan 
10435b54dac8SHaiyang Zhang 	ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
10445b54dac8SHaiyang Zhang 			 nvscdev->ring_size * PAGE_SIZE, NULL, 0,
10456de38af6Sstephen hemminger 			 netvsc_channel_cb, nvchan);
104676bb5db5Sstephen hemminger 	if (ret == 0)
10476de38af6Sstephen hemminger 		napi_enable(&nvchan->napi);
104876bb5db5Sstephen hemminger 	else
10498195b139SStephen Hemminger 		netdev_notice(ndev, "sub channel open failed: %d\n", ret);
105015a863bfSstephen hemminger 
10518f2bb1deSStephen Hemminger 	if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn)
1052732e4985Sstephen hemminger 		wake_up(&nvscdev->subchan_open);
10535b54dac8SHaiyang Zhang }
10545b54dac8SHaiyang Zhang 
10558195b139SStephen Hemminger /* Open sub-channels after completing the handling of the device probe.
10568195b139SStephen Hemminger  * This breaks overlap of processing the host message for the
10578195b139SStephen Hemminger  * new primary channel with the initialization of sub-channels.
10588195b139SStephen Hemminger  */
10598195b139SStephen Hemminger void rndis_set_subchannel(struct work_struct *w)
10608195b139SStephen Hemminger {
10618195b139SStephen Hemminger 	struct netvsc_device *nvdev
10628195b139SStephen Hemminger 		= container_of(w, struct netvsc_device, subchan_work);
10638195b139SStephen Hemminger 	struct nvsp_message *init_packet = &nvdev->channel_init_pkt;
10648195b139SStephen Hemminger 	struct net_device_context *ndev_ctx;
10658195b139SStephen Hemminger 	struct rndis_device *rdev;
10668195b139SStephen Hemminger 	struct net_device *ndev;
10678195b139SStephen Hemminger 	struct hv_device *hv_dev;
10688195b139SStephen Hemminger 	int i, ret;
10698195b139SStephen Hemminger 
10708195b139SStephen Hemminger 	if (!rtnl_trylock()) {
10718195b139SStephen Hemminger 		schedule_work(w);
10728195b139SStephen Hemminger 		return;
10738195b139SStephen Hemminger 	}
10748195b139SStephen Hemminger 
10758195b139SStephen Hemminger 	rdev = nvdev->extension;
10768195b139SStephen Hemminger 	if (!rdev)
10778195b139SStephen Hemminger 		goto unlock;	/* device was removed */
10788195b139SStephen Hemminger 
10798195b139SStephen Hemminger 	ndev = rdev->ndev;
10808195b139SStephen Hemminger 	ndev_ctx = netdev_priv(ndev);
10818195b139SStephen Hemminger 	hv_dev = ndev_ctx->device_ctx;
10828195b139SStephen Hemminger 
10838195b139SStephen Hemminger 	memset(init_packet, 0, sizeof(struct nvsp_message));
10848195b139SStephen Hemminger 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
10858195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
10868195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
10878195b139SStephen Hemminger 						nvdev->num_chn - 1;
10888195b139SStephen Hemminger 	ret = vmbus_sendpacket(hv_dev->channel, init_packet,
10898195b139SStephen Hemminger 			       sizeof(struct nvsp_message),
10908195b139SStephen Hemminger 			       (unsigned long)init_packet,
10918195b139SStephen Hemminger 			       VM_PKT_DATA_INBAND,
10928195b139SStephen Hemminger 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
10938195b139SStephen Hemminger 	if (ret) {
10948195b139SStephen Hemminger 		netdev_err(ndev, "sub channel allocate send failed: %d\n", ret);
10958195b139SStephen Hemminger 		goto failed;
10968195b139SStephen Hemminger 	}
10978195b139SStephen Hemminger 
10988195b139SStephen Hemminger 	wait_for_completion(&nvdev->channel_init_wait);
10998195b139SStephen Hemminger 	if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
11008195b139SStephen Hemminger 		netdev_err(ndev, "sub channel request failed\n");
11018195b139SStephen Hemminger 		goto failed;
11028195b139SStephen Hemminger 	}
11038195b139SStephen Hemminger 
11048195b139SStephen Hemminger 	nvdev->num_chn = 1 +
11058195b139SStephen Hemminger 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
11068195b139SStephen Hemminger 
11078195b139SStephen Hemminger 	/* wait for all sub channels to open */
11088195b139SStephen Hemminger 	wait_event(nvdev->subchan_open,
11098195b139SStephen Hemminger 		   atomic_read(&nvdev->open_chn) == nvdev->num_chn);
11108195b139SStephen Hemminger 
11118195b139SStephen Hemminger 	/* ignore failues from setting rss parameters, still have channels */
11128195b139SStephen Hemminger 	rndis_filter_set_rss_param(rdev, netvsc_hash_key);
11138195b139SStephen Hemminger 
11148195b139SStephen Hemminger 	netif_set_real_num_tx_queues(ndev, nvdev->num_chn);
11158195b139SStephen Hemminger 	netif_set_real_num_rx_queues(ndev, nvdev->num_chn);
11168195b139SStephen Hemminger 
1117a6fb6aa3SHaiyang Zhang 	for (i = 0; i < VRSS_SEND_TAB_SIZE; i++)
1118a6fb6aa3SHaiyang Zhang 		ndev_ctx->tx_table[i] = i % nvdev->num_chn;
1119a6fb6aa3SHaiyang Zhang 
11208195b139SStephen Hemminger 	rtnl_unlock();
11218195b139SStephen Hemminger 	return;
11228195b139SStephen Hemminger 
11238195b139SStephen Hemminger failed:
11248195b139SStephen Hemminger 	/* fallback to only primary channel */
11258195b139SStephen Hemminger 	for (i = 1; i < nvdev->num_chn; i++)
11268195b139SStephen Hemminger 		netif_napi_del(&nvdev->chan_table[i].napi);
11278195b139SStephen Hemminger 
11288195b139SStephen Hemminger 	nvdev->max_chn = 1;
11298195b139SStephen Hemminger 	nvdev->num_chn = 1;
11308195b139SStephen Hemminger unlock:
11318195b139SStephen Hemminger 	rtnl_unlock();
11328195b139SStephen Hemminger }
11338195b139SStephen Hemminger 
11349749fed5Sstephen hemminger struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
11352c7f83caSstephen hemminger 				      struct netvsc_device_info *device_info)
113695fa0405SHaiyang Zhang {
11373d541ac5SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
11383d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
113995fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
114095fa0405SHaiyang Zhang 	struct rndis_device *rndis_device;
114123312a3bSstephen hemminger 	struct ndis_offload hwcaps;
11424a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
11435b54dac8SHaiyang Zhang 	struct ndis_recv_scale_cap rsscap;
11445b54dac8SHaiyang Zhang 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
114523312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
11465c4217d0SHaiyang Zhang 	u32 mtu, size;
1147e01ec219SKY Srinivasan 	const struct cpumask *node_cpu_mask;
1148e01ec219SKY Srinivasan 	u32 num_possible_rss_qs;
1149ff4a4419Sstephen hemminger 	int i, ret;
115095fa0405SHaiyang Zhang 
115195fa0405SHaiyang Zhang 	rndis_device = get_rndis_device();
115295fa0405SHaiyang Zhang 	if (!rndis_device)
11539749fed5Sstephen hemminger 		return ERR_PTR(-ENODEV);
115495fa0405SHaiyang Zhang 
115595fa0405SHaiyang Zhang 	/*
115695fa0405SHaiyang Zhang 	 * Let the inner driver handle this first to create the netvsc channel
115795fa0405SHaiyang Zhang 	 * NOTE! Once the channel is created, we may get a receive callback
115895fa0405SHaiyang Zhang 	 * (RndisFilterOnReceive()) before this call is completed
115995fa0405SHaiyang Zhang 	 */
11609749fed5Sstephen hemminger 	net_device = netvsc_device_add(dev, device_info);
11619749fed5Sstephen hemminger 	if (IS_ERR(net_device)) {
116295fa0405SHaiyang Zhang 		kfree(rndis_device);
11639749fed5Sstephen hemminger 		return net_device;
116495fa0405SHaiyang Zhang 	}
116595fa0405SHaiyang Zhang 
116695fa0405SHaiyang Zhang 	/* Initialize the rndis device */
116759995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
11685b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
116995fa0405SHaiyang Zhang 
117095fa0405SHaiyang Zhang 	net_device->extension = rndis_device;
11713d541ac5SVitaly Kuznetsov 	rndis_device->ndev = net;
117295fa0405SHaiyang Zhang 
117395fa0405SHaiyang Zhang 	/* Send the rndis initialization message */
1174867047c4Sstephen hemminger 	ret = rndis_filter_init_device(rndis_device, net_device);
11759749fed5Sstephen hemminger 	if (ret != 0)
11769749fed5Sstephen hemminger 		goto err_dev_remv;
117795fa0405SHaiyang Zhang 
11784d3c9d37SHaiyang Zhang 	/* Get the MTU from the host */
11794d3c9d37SHaiyang Zhang 	size = sizeof(u32);
1180867047c4Sstephen hemminger 	ret = rndis_filter_query_device(rndis_device, net_device,
11814d3c9d37SHaiyang Zhang 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
11824d3c9d37SHaiyang Zhang 					&mtu, &size);
11830a1275caSVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
11840a1275caSVitaly Kuznetsov 		net->mtu = mtu;
11854d3c9d37SHaiyang Zhang 
118695fa0405SHaiyang Zhang 	/* Get the mac address */
1187867047c4Sstephen hemminger 	ret = rndis_filter_query_device_mac(rndis_device, net_device);
11889749fed5Sstephen hemminger 	if (ret != 0)
11899749fed5Sstephen hemminger 		goto err_dev_remv;
119095fa0405SHaiyang Zhang 
119195fa0405SHaiyang Zhang 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
119295fa0405SHaiyang Zhang 
119323312a3bSstephen hemminger 	/* Find HW offload capabilities */
1194867047c4Sstephen hemminger 	ret = rndis_query_hwcaps(rndis_device, net_device, &hwcaps);
11959749fed5Sstephen hemminger 	if (ret != 0)
11969749fed5Sstephen hemminger 		goto err_dev_remv;
119723312a3bSstephen hemminger 
119823312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
11994a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
120023312a3bSstephen hemminger 
120123312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
120223312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
120323312a3bSstephen hemminger 
120423312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
120523312a3bSstephen hemminger 	net->hw_features = NETIF_F_RXCSUM;
120623312a3bSstephen hemminger 
120723312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
120823312a3bSstephen hemminger 		/* Can checksum TCP */
120923312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
121023312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
121123312a3bSstephen hemminger 
12124a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
121323312a3bSstephen hemminger 
121423312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
12154a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
121623312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
121723312a3bSstephen hemminger 
121823312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
121923312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
122023312a3bSstephen hemminger 		}
122123312a3bSstephen hemminger 
122223312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
122323312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
122423312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
122523312a3bSstephen hemminger 		}
122623312a3bSstephen hemminger 	}
122723312a3bSstephen hemminger 
122823312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
122923312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
123023312a3bSstephen hemminger 
123123312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
123223312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
123323312a3bSstephen hemminger 
123423312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
123523312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
123623312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
123723312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
123823312a3bSstephen hemminger 
123923312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
124023312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
124123312a3bSstephen hemminger 		}
124223312a3bSstephen hemminger 
124323312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
124423312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
124523312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
124623312a3bSstephen hemminger 		}
124723312a3bSstephen hemminger 	}
124823312a3bSstephen hemminger 
124923312a3bSstephen hemminger 	netif_set_gso_max_size(net, gso_max_size);
12504a0e70aeSKY Srinivasan 
12519749fed5Sstephen hemminger 	ret = rndis_filter_set_offload_params(net, net_device, &offloads);
12524a0e70aeSKY Srinivasan 	if (ret)
12534a0e70aeSKY Srinivasan 		goto err_dev_remv;
12544a0e70aeSKY Srinivasan 
1255867047c4Sstephen hemminger 	rndis_filter_query_device_link_status(rndis_device, net_device);
125695fa0405SHaiyang Zhang 
125793ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
125895fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
1259dedb459eSHaiyang Zhang 		   rndis_device->link_state ? "down" : "up");
126095fa0405SHaiyang Zhang 
12615b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
12629749fed5Sstephen hemminger 		return net_device;
12635b54dac8SHaiyang Zhang 
1264867047c4Sstephen hemminger 	rndis_filter_query_link_speed(rndis_device, net_device);
1265b37879e6SHaiyang Zhang 
12665b54dac8SHaiyang Zhang 	/* vRSS setup */
12675b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
1268867047c4Sstephen hemminger 	ret = rndis_filter_query_device(rndis_device, net_device,
12695b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
12705b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
12715b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
12725b54dac8SHaiyang Zhang 		goto out;
12735b54dac8SHaiyang Zhang 
1274e01ec219SKY Srinivasan 	/*
1275e01ec219SKY Srinivasan 	 * We will limit the VRSS channels to the number CPUs in the NUMA node
1276e01ec219SKY Srinivasan 	 * the primary channel is currently bound to.
12773071ada4Sstephen hemminger 	 *
12783071ada4Sstephen hemminger 	 * This also guarantees that num_possible_rss_qs <= num_online_cpus
1279e01ec219SKY Srinivasan 	 */
1280e01ec219SKY Srinivasan 	node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu));
12813071ada4Sstephen hemminger 	num_possible_rss_qs = min_t(u32, cpumask_weight(node_cpu_mask),
12823071ada4Sstephen hemminger 				    rsscap.num_recv_que);
12833071ada4Sstephen hemminger 
12843071ada4Sstephen hemminger 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs);
12858ebdcc52SAndrew Schwartzmeyer 
12868ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
12873071ada4Sstephen hemminger 	net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
1288ff4a4419Sstephen hemminger 
1289ff4a4419Sstephen hemminger 	for (i = 0; i < ITAB_NUM; i++)
129047371300SHaiyang Zhang 		rndis_device->rx_table[i] = ethtool_rxfh_indir_default(
129147371300SHaiyang Zhang 						i, net_device->num_chn);
1292ff4a4419Sstephen hemminger 
1293732e4985Sstephen hemminger 	atomic_set(&net_device->open_chn, 1);
12948195b139SStephen Hemminger 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
12955b54dac8SHaiyang Zhang 
12967426b1a5Sstephen hemminger 	for (i = 1; i < net_device->num_chn; i++) {
12977426b1a5Sstephen hemminger 		ret = netvsc_alloc_recv_comp_ring(net_device, i);
12987426b1a5Sstephen hemminger 		if (ret) {
12997426b1a5Sstephen hemminger 			while (--i != 0)
13007426b1a5Sstephen hemminger 				vfree(net_device->chan_table[i].mrc.slots);
13017426b1a5Sstephen hemminger 			goto out;
13027426b1a5Sstephen hemminger 		}
13037426b1a5Sstephen hemminger 	}
13047426b1a5Sstephen hemminger 
13058195b139SStephen Hemminger 	for (i = 1; i < net_device->num_chn; i++)
13068195b139SStephen Hemminger 		netif_napi_add(net, &net_device->chan_table[i].napi,
13078195b139SStephen Hemminger 			       netvsc_poll, NAPI_POLL_WEIGHT);
13085b54dac8SHaiyang Zhang 
13098195b139SStephen Hemminger 	if (net_device->num_chn > 1)
13108195b139SStephen Hemminger 		schedule_work(&net_device->subchan_work);
13115362855aSVitaly Kuznetsov 
13125b54dac8SHaiyang Zhang out:
13138195b139SStephen Hemminger 	/* if unavailable, just proceed with one queue */
131459995370SAndrew Schwartzmeyer 	if (ret) {
131559995370SAndrew Schwartzmeyer 		net_device->max_chn = 1;
13165b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
131759995370SAndrew Schwartzmeyer 	}
1318b3e6b82aSKY Srinivasan 
13199749fed5Sstephen hemminger 	return net_device;
13204a0e70aeSKY Srinivasan 
13214a0e70aeSKY Srinivasan err_dev_remv:
13222289f0aaSstephen hemminger 	rndis_filter_device_remove(dev, net_device);
13239749fed5Sstephen hemminger 	return ERR_PTR(ret);
132495fa0405SHaiyang Zhang }
132595fa0405SHaiyang Zhang 
13262289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev,
13272289f0aaSstephen hemminger 				struct netvsc_device *net_dev)
132895fa0405SHaiyang Zhang {
132995fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1330d66ab514SHaiyang Zhang 
133195fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
133295fa0405SHaiyang Zhang 	rndis_filter_halt_device(rndis_dev);
133395fa0405SHaiyang Zhang 
133495fa0405SHaiyang Zhang 	net_dev->extension = NULL;
133595fa0405SHaiyang Zhang 
133695fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
13378195b139SStephen Hemminger 	kfree(rndis_dev);
133895fa0405SHaiyang Zhang }
133995fa0405SHaiyang Zhang 
13402f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
134195fa0405SHaiyang Zhang {
13422f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
134395fa0405SHaiyang Zhang 		return -EINVAL;
134495fa0405SHaiyang Zhang 
13452f5fa6c8SVitaly Kuznetsov 	if (atomic_inc_return(&nvdev->open_cnt) != 1)
134684bf9cefSKY Srinivasan 		return 0;
134784bf9cefSKY Srinivasan 
13482f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
134995fa0405SHaiyang Zhang }
135095fa0405SHaiyang Zhang 
13512f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
135295fa0405SHaiyang Zhang {
13535fccab3bSHaiyang Zhang 	if (!nvdev)
135495fa0405SHaiyang Zhang 		return -EINVAL;
135595fa0405SHaiyang Zhang 
135684bf9cefSKY Srinivasan 	if (atomic_dec_return(&nvdev->open_cnt) != 0)
135784bf9cefSKY Srinivasan 		return 0;
135884bf9cefSKY Srinivasan 
13595fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
136095fa0405SHaiyang Zhang }
1361ea383bf1Sstephen hemminger 
1362ea383bf1Sstephen hemminger bool rndis_filter_opened(const struct netvsc_device *nvdev)
1363ea383bf1Sstephen hemminger {
1364ea383bf1Sstephen hemminger 	return atomic_read(&nvdev->open_cnt) > 0;
1365ea383bf1Sstephen hemminger }
1366