19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
295fa0405SHaiyang Zhang /*
395fa0405SHaiyang Zhang  * Copyright (c) 2009, Microsoft Corporation.
495fa0405SHaiyang Zhang  *
595fa0405SHaiyang Zhang  * Authors:
695fa0405SHaiyang Zhang  *   Haiyang Zhang <haiyangz@microsoft.com>
795fa0405SHaiyang Zhang  *   Hank Janssen  <hjanssen@microsoft.com>
895fa0405SHaiyang Zhang  */
9cc69837fSJakub Kicinski #include <linux/ethtool.h>
1095fa0405SHaiyang Zhang #include <linux/kernel.h>
1195fa0405SHaiyang Zhang #include <linux/sched.h>
1295fa0405SHaiyang Zhang #include <linux/wait.h>
1395fa0405SHaiyang Zhang #include <linux/highmem.h>
1495fa0405SHaiyang Zhang #include <linux/slab.h>
1595fa0405SHaiyang Zhang #include <linux/io.h>
1695fa0405SHaiyang Zhang #include <linux/if_ether.h>
1795fa0405SHaiyang Zhang #include <linux/netdevice.h>
181f5f3a75SHaiyang Zhang #include <linux/if_vlan.h>
191ce09e89SHaiyang Zhang #include <linux/nls.h>
20d6472302SStephen Rothwell #include <linux/vmalloc.h>
2127f5aa92Sstephen hemminger #include <linux/rtnetlink.h>
220fe554a4SStephen Hemminger #include <linux/ucs2_string.h>
2395fa0405SHaiyang Zhang 
2495fa0405SHaiyang Zhang #include "hyperv_net.h"
25ec966381SStephen Hemminger #include "netvsc_trace.h"
2695fa0405SHaiyang Zhang 
274f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w);
2895fa0405SHaiyang Zhang 
2911d8620eSBoqun Feng #define RNDIS_EXT_LEN HV_HYP_PAGE_SIZE
3095fa0405SHaiyang Zhang struct rndis_request {
3195fa0405SHaiyang Zhang 	struct list_head list_ent;
3295fa0405SHaiyang Zhang 	struct completion  wait_event;
3395fa0405SHaiyang Zhang 
3495fa0405SHaiyang Zhang 	struct rndis_message response_msg;
35a3a6cab5SHaiyang Zhang 	/*
36a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
37a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
38a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
39a3a6cab5SHaiyang Zhang 	 * future.
40a3a6cab5SHaiyang Zhang 	 */
41a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
4295fa0405SHaiyang Zhang 
4395fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
4495fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
450f48917bSHaiyang Zhang 
4695fa0405SHaiyang Zhang 	struct rndis_message request_msg;
470f48917bSHaiyang Zhang 	/*
48a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
49a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
500f48917bSHaiyang Zhang 	 */
51a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
5295fa0405SHaiyang Zhang };
5395fa0405SHaiyang Zhang 
54962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
55962f3feeSstephen hemminger 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
56962f3feeSstephen hemminger 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
57962f3feeSstephen hemminger 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
58962f3feeSstephen hemminger 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
59962f3feeSstephen hemminger 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
60962f3feeSstephen hemminger };
61962f3feeSstephen hemminger 
6295fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
6395fa0405SHaiyang Zhang {
6495fa0405SHaiyang Zhang 	struct rndis_device *device;
6595fa0405SHaiyang Zhang 
6695fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
6795fa0405SHaiyang Zhang 	if (!device)
6895fa0405SHaiyang Zhang 		return NULL;
6995fa0405SHaiyang Zhang 
7095fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
7195fa0405SHaiyang Zhang 
7295fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
734f19c0d8Sstephen hemminger 	INIT_WORK(&device->mcast_work, rndis_set_multicast);
7495fa0405SHaiyang Zhang 
7595fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
7695fa0405SHaiyang Zhang 
7795fa0405SHaiyang Zhang 	return device;
7895fa0405SHaiyang Zhang }
7995fa0405SHaiyang Zhang 
8095fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
8195fa0405SHaiyang Zhang 					     u32 msg_type,
8295fa0405SHaiyang Zhang 					     u32 msg_len)
8395fa0405SHaiyang Zhang {
8495fa0405SHaiyang Zhang 	struct rndis_request *request;
8595fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
8695fa0405SHaiyang Zhang 	struct rndis_set_request *set;
8795fa0405SHaiyang Zhang 	unsigned long flags;
8895fa0405SHaiyang Zhang 
8995fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
9095fa0405SHaiyang Zhang 	if (!request)
9195fa0405SHaiyang Zhang 		return NULL;
9295fa0405SHaiyang Zhang 
9395fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
9495fa0405SHaiyang Zhang 
9595fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
9695fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
9795fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
9895fa0405SHaiyang Zhang 
995b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1005b54dac8SHaiyang Zhang 
10195fa0405SHaiyang Zhang 	/*
10295fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
10395fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
10495fa0405SHaiyang Zhang 	 * template
10595fa0405SHaiyang Zhang 	 */
10695fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
10795fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
10895fa0405SHaiyang Zhang 
10995fa0405SHaiyang Zhang 	/* Add to the request list */
11095fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
11195fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
11295fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
11395fa0405SHaiyang Zhang 
11495fa0405SHaiyang Zhang 	return request;
11595fa0405SHaiyang Zhang }
11695fa0405SHaiyang Zhang 
11795fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
11895fa0405SHaiyang Zhang 			    struct rndis_request *req)
11995fa0405SHaiyang Zhang {
12095fa0405SHaiyang Zhang 	unsigned long flags;
12195fa0405SHaiyang Zhang 
12295fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12395fa0405SHaiyang Zhang 	list_del(&req->list_ent);
12495fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12595fa0405SHaiyang Zhang 
12695fa0405SHaiyang Zhang 	kfree(req);
12795fa0405SHaiyang Zhang }
12895fa0405SHaiyang Zhang 
12979cf1baeSStephen Hemminger static void dump_rndis_message(struct net_device *netdev,
1300ba35fe9SAndrea Parri (Microsoft) 			       const struct rndis_message *rndis_msg,
1310ba35fe9SAndrea Parri (Microsoft) 			       const void *data)
13295fa0405SHaiyang Zhang {
13395fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
13451491167SLinus Walleij 	case RNDIS_MSG_PACKET:
135505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= sizeof(struct rndis_packet)) {
1360ba35fe9SAndrea Parri (Microsoft) 			const struct rndis_packet *pkt = data + RNDIS_HEADER_SIZE;
13751491167SLinus Walleij 			netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
13895fa0405SHaiyang Zhang 				   "data offset %u data len %u, # oob %u, "
13995fa0405SHaiyang Zhang 				   "oob offset %u, oob len %u, pkt offset %u, "
14095fa0405SHaiyang Zhang 				   "pkt len %u\n",
14195fa0405SHaiyang Zhang 				   rndis_msg->msg_len,
142505e3f00SAndrea Parri (Microsoft) 				   pkt->data_offset,
143505e3f00SAndrea Parri (Microsoft) 				   pkt->data_len,
144505e3f00SAndrea Parri (Microsoft) 				   pkt->num_oob_data_elements,
145505e3f00SAndrea Parri (Microsoft) 				   pkt->oob_data_offset,
146505e3f00SAndrea Parri (Microsoft) 				   pkt->oob_data_len,
147505e3f00SAndrea Parri (Microsoft) 				   pkt->per_pkt_info_offset,
148505e3f00SAndrea Parri (Microsoft) 				   pkt->per_pkt_info_len);
149505e3f00SAndrea Parri (Microsoft) 		}
15095fa0405SHaiyang Zhang 		break;
15195fa0405SHaiyang Zhang 
15251491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
153505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
154505e3f00SAndrea Parri (Microsoft) 				sizeof(struct rndis_initialize_complete)) {
155505e3f00SAndrea Parri (Microsoft) 			const struct rndis_initialize_complete *init_complete =
1560ba35fe9SAndrea Parri (Microsoft) 				data + RNDIS_HEADER_SIZE;
15751491167SLinus Walleij 			netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
15895fa0405SHaiyang Zhang 				"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
15995fa0405SHaiyang Zhang 				"device flags %d, max xfer size 0x%x, max pkts %u, "
16095fa0405SHaiyang Zhang 				"pkt aligned %u)\n",
16195fa0405SHaiyang Zhang 				rndis_msg->msg_len,
162505e3f00SAndrea Parri (Microsoft) 				init_complete->req_id,
163505e3f00SAndrea Parri (Microsoft) 				init_complete->status,
164505e3f00SAndrea Parri (Microsoft) 				init_complete->major_ver,
165505e3f00SAndrea Parri (Microsoft) 				init_complete->minor_ver,
166505e3f00SAndrea Parri (Microsoft) 				init_complete->dev_flags,
167505e3f00SAndrea Parri (Microsoft) 				init_complete->max_xfer_size,
168505e3f00SAndrea Parri (Microsoft) 				init_complete->max_pkt_per_msg,
169505e3f00SAndrea Parri (Microsoft) 				init_complete->pkt_alignment_factor);
170505e3f00SAndrea Parri (Microsoft) 		}
17195fa0405SHaiyang Zhang 		break;
17295fa0405SHaiyang Zhang 
17351491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
174505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
175505e3f00SAndrea Parri (Microsoft) 				sizeof(struct rndis_query_complete)) {
176505e3f00SAndrea Parri (Microsoft) 			const struct rndis_query_complete *query_complete =
1770ba35fe9SAndrea Parri (Microsoft) 				data + RNDIS_HEADER_SIZE;
17851491167SLinus Walleij 			netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17995fa0405SHaiyang Zhang 				"(len %u, id 0x%x, status 0x%x, buf len %u, "
18095fa0405SHaiyang Zhang 				"buf offset %u)\n",
18195fa0405SHaiyang Zhang 				rndis_msg->msg_len,
182505e3f00SAndrea Parri (Microsoft) 				query_complete->req_id,
183505e3f00SAndrea Parri (Microsoft) 				query_complete->status,
184505e3f00SAndrea Parri (Microsoft) 				query_complete->info_buflen,
185505e3f00SAndrea Parri (Microsoft) 				query_complete->info_buf_offset);
186505e3f00SAndrea Parri (Microsoft) 		}
18795fa0405SHaiyang Zhang 		break;
18895fa0405SHaiyang Zhang 
18951491167SLinus Walleij 	case RNDIS_MSG_SET_C:
190505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE + sizeof(struct rndis_set_complete)) {
191505e3f00SAndrea Parri (Microsoft) 			const struct rndis_set_complete *set_complete =
1920ba35fe9SAndrea Parri (Microsoft) 				data + RNDIS_HEADER_SIZE;
19395fa0405SHaiyang Zhang 			netdev_dbg(netdev,
19451491167SLinus Walleij 				"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
19595fa0405SHaiyang Zhang 				rndis_msg->msg_len,
196505e3f00SAndrea Parri (Microsoft) 				set_complete->req_id,
197505e3f00SAndrea Parri (Microsoft) 				set_complete->status);
198505e3f00SAndrea Parri (Microsoft) 		}
19995fa0405SHaiyang Zhang 		break;
20095fa0405SHaiyang Zhang 
20151491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
202505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
203505e3f00SAndrea Parri (Microsoft) 				sizeof(struct rndis_indicate_status)) {
204505e3f00SAndrea Parri (Microsoft) 			const struct rndis_indicate_status *indicate_status =
2050ba35fe9SAndrea Parri (Microsoft) 				data + RNDIS_HEADER_SIZE;
20651491167SLinus Walleij 			netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
20795fa0405SHaiyang Zhang 				"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
20895fa0405SHaiyang Zhang 				rndis_msg->msg_len,
209505e3f00SAndrea Parri (Microsoft) 				indicate_status->status,
210505e3f00SAndrea Parri (Microsoft) 				indicate_status->status_buflen,
211505e3f00SAndrea Parri (Microsoft) 				indicate_status->status_buf_offset);
212505e3f00SAndrea Parri (Microsoft) 		}
21395fa0405SHaiyang Zhang 		break;
21495fa0405SHaiyang Zhang 
21595fa0405SHaiyang Zhang 	default:
21695fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
21795fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
21895fa0405SHaiyang Zhang 			rndis_msg->msg_len);
21995fa0405SHaiyang Zhang 		break;
22095fa0405SHaiyang Zhang 	}
22195fa0405SHaiyang Zhang }
22295fa0405SHaiyang Zhang 
22395fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
22495fa0405SHaiyang Zhang 				  struct rndis_request *req)
22595fa0405SHaiyang Zhang {
22695fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
227b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
228a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
22902b6de01Sstephen hemminger 	int ret;
23095fa0405SHaiyang Zhang 
23195fa0405SHaiyang Zhang 	/* Setup the packet to send it */
23295fa0405SHaiyang Zhang 	packet = &req->pkt;
23395fa0405SHaiyang Zhang 
23495fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
23595fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
23695fa0405SHaiyang Zhang 
237a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
23811d8620eSBoqun Feng 					HV_HYP_PAGE_SHIFT;
239a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
24011d8620eSBoqun Feng 	pb[0].offset = offset_in_hvpage(&req->request_msg);
24195fa0405SHaiyang Zhang 
24299e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
24311d8620eSBoqun Feng 	if (pb[0].offset + pb[0].len > HV_HYP_PAGE_SIZE) {
24499e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
24511d8620eSBoqun Feng 		pb[0].len = HV_HYP_PAGE_SIZE -
246a9f2e2d6SKY Srinivasan 			pb[0].offset;
247a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
24811d8620eSBoqun Feng 			+ pb[0].len) >> HV_HYP_PAGE_SHIFT;
249a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
250a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
251a9f2e2d6SKY Srinivasan 			pb[0].len;
25299e3fcfaSHaiyang Zhang 	}
25399e3fcfaSHaiyang Zhang 
254ec966381SStephen Hemminger 	trace_rndis_send(dev->ndev, 0, &req->request_msg);
255ec966381SStephen Hemminger 
256867047c4Sstephen hemminger 	rcu_read_lock_bh();
257351e1581SHaiyang Zhang 	ret = netvsc_send(dev->ndev, packet, NULL, pb, NULL, false);
258867047c4Sstephen hemminger 	rcu_read_unlock_bh();
259867047c4Sstephen hemminger 
26095fa0405SHaiyang Zhang 	return ret;
26195fa0405SHaiyang Zhang }
26295fa0405SHaiyang Zhang 
2631b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2641b07da51SHaiyang Zhang 				 struct rndis_request *request)
2651b07da51SHaiyang Zhang {
2661b07da51SHaiyang Zhang 	u32 link_status;
2671b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
268505e3f00SAndrea Parri (Microsoft) 	u32 msg_len = request->response_msg.msg_len;
269505e3f00SAndrea Parri (Microsoft) 
270505e3f00SAndrea Parri (Microsoft) 	/* Ensure the packet is big enough to access its fields */
271505e3f00SAndrea Parri (Microsoft) 	if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete))
272505e3f00SAndrea Parri (Microsoft) 		return;
2731b07da51SHaiyang Zhang 
2741b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2751b07da51SHaiyang Zhang 
2761b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
277505e3f00SAndrea Parri (Microsoft) 	    query_complete->info_buflen >= sizeof(u32) &&
278505e3f00SAndrea Parri (Microsoft) 	    query_complete->info_buf_offset >= sizeof(*query_complete) &&
279505e3f00SAndrea Parri (Microsoft) 	    msg_len - RNDIS_HEADER_SIZE >= query_complete->info_buf_offset &&
280505e3f00SAndrea Parri (Microsoft) 	    msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset
281505e3f00SAndrea Parri (Microsoft) 			>= query_complete->info_buflen) {
2821b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2831b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2841b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2851b07da51SHaiyang Zhang 	}
2861b07da51SHaiyang Zhang }
2871b07da51SHaiyang Zhang 
28802400fceSStephen Hemminger static void rndis_filter_receive_response(struct net_device *ndev,
28902400fceSStephen Hemminger 					  struct netvsc_device *nvdev,
2900ba35fe9SAndrea Parri (Microsoft) 					  struct rndis_message *resp,
2910ba35fe9SAndrea Parri (Microsoft) 					  void *data)
29295fa0405SHaiyang Zhang {
2930ba35fe9SAndrea Parri (Microsoft) 	u32 *req_id = &resp->msg.init_complete.req_id;
29402400fceSStephen Hemminger 	struct rndis_device *dev = nvdev->extension;
29595fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
29695fa0405SHaiyang Zhang 	bool found = false;
29795fa0405SHaiyang Zhang 	unsigned long flags;
29802400fceSStephen Hemminger 
29902400fceSStephen Hemminger 	/* This should never happen, it means control message
30002400fceSStephen Hemminger 	 * response received after device removed.
30102400fceSStephen Hemminger 	 */
30202400fceSStephen Hemminger 	if (dev->state == RNDIS_DEV_UNINITIALIZED) {
30302400fceSStephen Hemminger 		netdev_err(ndev,
30402400fceSStephen Hemminger 			   "got rndis message uninitialized\n");
30502400fceSStephen Hemminger 		return;
30602400fceSStephen Hemminger 	}
30795fa0405SHaiyang Zhang 
30844144185SAndres Beltran 	/* Ensure the packet is big enough to read req_id. Req_id is the 1st
30944144185SAndres Beltran 	 * field in any request/response message, so the payload should have at
31044144185SAndres Beltran 	 * least sizeof(u32) bytes
31144144185SAndres Beltran 	 */
31244144185SAndres Beltran 	if (resp->msg_len - RNDIS_HEADER_SIZE < sizeof(u32)) {
31344144185SAndres Beltran 		netdev_err(ndev, "rndis msg_len too small: %u\n",
31444144185SAndres Beltran 			   resp->msg_len);
31544144185SAndres Beltran 		return;
31644144185SAndres Beltran 	}
31744144185SAndres Beltran 
3180ba35fe9SAndrea Parri (Microsoft) 	/* Copy the request ID into nvchan->recv_buf */
3190ba35fe9SAndrea Parri (Microsoft) 	*req_id = *(u32 *)(data + RNDIS_HEADER_SIZE);
3200ba35fe9SAndrea Parri (Microsoft) 
32195fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
32295fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
32395fa0405SHaiyang Zhang 		/*
32495fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
32595fa0405SHaiyang Zhang 		 * field
32695fa0405SHaiyang Zhang 		 */
3270ba35fe9SAndrea Parri (Microsoft) 		if (request->request_msg.msg.init_req.req_id == *req_id) {
32895fa0405SHaiyang Zhang 			found = true;
32995fa0405SHaiyang Zhang 			break;
33095fa0405SHaiyang Zhang 		}
33195fa0405SHaiyang Zhang 	}
33295fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
33395fa0405SHaiyang Zhang 
33495fa0405SHaiyang Zhang 	if (found) {
335a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
336a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
3370ba35fe9SAndrea Parri (Microsoft) 			memcpy(&request->response_msg, resp, RNDIS_HEADER_SIZE + sizeof(*req_id));
3380ba35fe9SAndrea Parri (Microsoft) 			memcpy((void *)&request->response_msg + RNDIS_HEADER_SIZE + sizeof(*req_id),
3390ba35fe9SAndrea Parri (Microsoft) 			       data + RNDIS_HEADER_SIZE + sizeof(*req_id),
3400ba35fe9SAndrea Parri (Microsoft) 			       resp->msg_len - RNDIS_HEADER_SIZE - sizeof(*req_id));
3411b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
3421b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
3431b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
3441b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
34595fa0405SHaiyang Zhang 		} else {
34695fa0405SHaiyang Zhang 			netdev_err(ndev,
34795fa0405SHaiyang Zhang 				"rndis response buffer overflow "
34895fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
34995fa0405SHaiyang Zhang 				resp->msg_len,
35086eedaccSKY Srinivasan 				sizeof(struct rndis_message));
35195fa0405SHaiyang Zhang 
35295fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
35351491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
35495fa0405SHaiyang Zhang 				/* does not have a request id field */
35595fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
356007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
35795fa0405SHaiyang Zhang 			} else {
35895fa0405SHaiyang Zhang 				request->response_msg.msg.
35995fa0405SHaiyang Zhang 				init_complete.status =
360007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
36195fa0405SHaiyang Zhang 			}
36295fa0405SHaiyang Zhang 		}
36395fa0405SHaiyang Zhang 
364846da38dSTianyu Lan 		netvsc_dma_unmap(((struct net_device_context *)
365846da38dSTianyu Lan 			netdev_priv(ndev))->device_ctx, &request->pkt);
36695fa0405SHaiyang Zhang 		complete(&request->wait_event);
36795fa0405SHaiyang Zhang 	} else {
36895fa0405SHaiyang Zhang 		netdev_err(ndev,
36995fa0405SHaiyang Zhang 			"no rndis request found for this response "
37095fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
3710ba35fe9SAndrea Parri (Microsoft) 			*req_id,
37295fa0405SHaiyang Zhang 			resp->ndis_msg_type);
37395fa0405SHaiyang Zhang 	}
37495fa0405SHaiyang Zhang }
37595fa0405SHaiyang Zhang 
3761f5f3a75SHaiyang Zhang /*
3771f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3781f5f3a75SHaiyang Zhang  * return NULL if not found.
3791f5f3a75SHaiyang Zhang  */
38044144185SAndres Beltran static inline void *rndis_get_ppi(struct net_device *ndev,
38144144185SAndres Beltran 				  struct rndis_packet *rpkt,
382505e3f00SAndrea Parri (Microsoft) 				  u32 rpkt_len, u32 type, u8 internal,
3830ba35fe9SAndrea Parri (Microsoft) 				  u32 ppi_size, void *data)
3841f5f3a75SHaiyang Zhang {
3851f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3861f5f3a75SHaiyang Zhang 	int len;
3871f5f3a75SHaiyang Zhang 
3881f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3891f5f3a75SHaiyang Zhang 		return NULL;
3901f5f3a75SHaiyang Zhang 
39144144185SAndres Beltran 	/* Validate info_offset and info_len */
39244144185SAndres Beltran 	if (rpkt->per_pkt_info_offset < sizeof(struct rndis_packet) ||
39344144185SAndres Beltran 	    rpkt->per_pkt_info_offset > rpkt_len) {
39444144185SAndres Beltran 		netdev_err(ndev, "Invalid per_pkt_info_offset: %u\n",
39544144185SAndres Beltran 			   rpkt->per_pkt_info_offset);
39644144185SAndres Beltran 		return NULL;
39744144185SAndres Beltran 	}
39844144185SAndres Beltran 
399505e3f00SAndrea Parri (Microsoft) 	if (rpkt->per_pkt_info_len < sizeof(*ppi) ||
400505e3f00SAndrea Parri (Microsoft) 	    rpkt->per_pkt_info_len > rpkt_len - rpkt->per_pkt_info_offset) {
40144144185SAndres Beltran 		netdev_err(ndev, "Invalid per_pkt_info_len: %u\n",
40244144185SAndres Beltran 			   rpkt->per_pkt_info_len);
40344144185SAndres Beltran 		return NULL;
40444144185SAndres Beltran 	}
40544144185SAndres Beltran 
4061f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
4071f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
4080ba35fe9SAndrea Parri (Microsoft) 	/* Copy the PPIs into nvchan->recv_buf */
4090ba35fe9SAndrea Parri (Microsoft) 	memcpy(ppi, data + RNDIS_HEADER_SIZE + rpkt->per_pkt_info_offset, rpkt->per_pkt_info_len);
4101f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
4111f5f3a75SHaiyang Zhang 
4121f5f3a75SHaiyang Zhang 	while (len > 0) {
41344144185SAndres Beltran 		/* Validate ppi_offset and ppi_size */
41444144185SAndres Beltran 		if (ppi->size > len) {
41544144185SAndres Beltran 			netdev_err(ndev, "Invalid ppi size: %u\n", ppi->size);
41644144185SAndres Beltran 			continue;
41744144185SAndres Beltran 		}
41844144185SAndres Beltran 
41944144185SAndres Beltran 		if (ppi->ppi_offset >= ppi->size) {
42044144185SAndres Beltran 			netdev_err(ndev, "Invalid ppi_offset: %u\n", ppi->ppi_offset);
42144144185SAndres Beltran 			continue;
42244144185SAndres Beltran 		}
42344144185SAndres Beltran 
424505e3f00SAndrea Parri (Microsoft) 		if (ppi->type == type && ppi->internal == internal) {
425505e3f00SAndrea Parri (Microsoft) 			/* ppi->size should be big enough to hold the returned object. */
426505e3f00SAndrea Parri (Microsoft) 			if (ppi->size - ppi->ppi_offset < ppi_size) {
427505e3f00SAndrea Parri (Microsoft) 				netdev_err(ndev, "Invalid ppi: size %u ppi_offset %u\n",
428505e3f00SAndrea Parri (Microsoft) 					   ppi->size, ppi->ppi_offset);
429505e3f00SAndrea Parri (Microsoft) 				continue;
430505e3f00SAndrea Parri (Microsoft) 			}
4311f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
432505e3f00SAndrea Parri (Microsoft) 		}
4331f5f3a75SHaiyang Zhang 		len -= ppi->size;
4341f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
4351f5f3a75SHaiyang Zhang 	}
4361f5f3a75SHaiyang Zhang 
4371f5f3a75SHaiyang Zhang 	return NULL;
4381f5f3a75SHaiyang Zhang }
4391f5f3a75SHaiyang Zhang 
440c8e4eff4SHaiyang Zhang static inline
441c8e4eff4SHaiyang Zhang void rsc_add_data(struct netvsc_channel *nvchan,
442c8e4eff4SHaiyang Zhang 		  const struct ndis_pkt_8021q_info *vlan,
443c8e4eff4SHaiyang Zhang 		  const struct ndis_tcp_ip_checksum_info *csum_info,
4441fac7ca4SStephen Hemminger 		  const u32 *hash_info,
445c8e4eff4SHaiyang Zhang 		  void *data, u32 len)
446c8e4eff4SHaiyang Zhang {
447c8e4eff4SHaiyang Zhang 	u32 cnt = nvchan->rsc.cnt;
448c8e4eff4SHaiyang Zhang 
449c8e4eff4SHaiyang Zhang 	if (cnt) {
450c8e4eff4SHaiyang Zhang 		nvchan->rsc.pktlen += len;
451c8e4eff4SHaiyang Zhang 	} else {
4520ba35fe9SAndrea Parri (Microsoft) 		/* The data/values pointed by vlan, csum_info and hash_info are shared
4530ba35fe9SAndrea Parri (Microsoft) 		 * across the different 'fragments' of the RSC packet; store them into
4540ba35fe9SAndrea Parri (Microsoft) 		 * the packet itself.
4550ba35fe9SAndrea Parri (Microsoft) 		 */
4560ba35fe9SAndrea Parri (Microsoft) 		if (vlan != NULL) {
4570ba35fe9SAndrea Parri (Microsoft) 			memcpy(&nvchan->rsc.vlan, vlan, sizeof(*vlan));
4580ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags |= NVSC_RSC_VLAN;
4590ba35fe9SAndrea Parri (Microsoft) 		} else {
4600ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags &= ~NVSC_RSC_VLAN;
4610ba35fe9SAndrea Parri (Microsoft) 		}
4620ba35fe9SAndrea Parri (Microsoft) 		if (csum_info != NULL) {
4630ba35fe9SAndrea Parri (Microsoft) 			memcpy(&nvchan->rsc.csum_info, csum_info, sizeof(*csum_info));
4640ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags |= NVSC_RSC_CSUM_INFO;
4650ba35fe9SAndrea Parri (Microsoft) 		} else {
4660ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags &= ~NVSC_RSC_CSUM_INFO;
4670ba35fe9SAndrea Parri (Microsoft) 		}
468c8e4eff4SHaiyang Zhang 		nvchan->rsc.pktlen = len;
4690ba35fe9SAndrea Parri (Microsoft) 		if (hash_info != NULL) {
4708dff9808SAndrea Parri (Microsoft) 			nvchan->rsc.hash_info = *hash_info;
4710ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags |= NVSC_RSC_HASH_INFO;
4720ba35fe9SAndrea Parri (Microsoft) 		} else {
4730ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags &= ~NVSC_RSC_HASH_INFO;
4740ba35fe9SAndrea Parri (Microsoft) 		}
475c8e4eff4SHaiyang Zhang 	}
476c8e4eff4SHaiyang Zhang 
477c8e4eff4SHaiyang Zhang 	nvchan->rsc.data[cnt] = data;
478c8e4eff4SHaiyang Zhang 	nvchan->rsc.len[cnt] = len;
479c8e4eff4SHaiyang Zhang 	nvchan->rsc.cnt++;
480c8e4eff4SHaiyang Zhang }
481c8e4eff4SHaiyang Zhang 
482dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
483345ac089SStephen Hemminger 				     struct netvsc_device *nvdev,
484c8e4eff4SHaiyang Zhang 				     struct netvsc_channel *nvchan,
4853be9b5fdSHaiyang Zhang 				     struct rndis_message *msg,
4860ba35fe9SAndrea Parri (Microsoft) 				     void *data, u32 data_buflen)
48795fa0405SHaiyang Zhang {
488dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
489dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
490dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
491c8e4eff4SHaiyang Zhang 	const struct rndis_pktinfo_id *pktinfo_id;
4921fac7ca4SStephen Hemminger 	const u32 *hash_info;
49344144185SAndres Beltran 	u32 data_offset, rpkt_len;
494c8e4eff4SHaiyang Zhang 	bool rsc_more = false;
495c8e4eff4SHaiyang Zhang 	int ret;
49695fa0405SHaiyang Zhang 
49744144185SAndres Beltran 	/* Ensure data_buflen is big enough to read header fields */
49844144185SAndres Beltran 	if (data_buflen < RNDIS_HEADER_SIZE + sizeof(struct rndis_packet)) {
49944144185SAndres Beltran 		netdev_err(ndev, "invalid rndis pkt, data_buflen too small: %u\n",
50044144185SAndres Beltran 			   data_buflen);
50144144185SAndres Beltran 		return NVSP_STAT_FAIL;
50244144185SAndres Beltran 	}
50344144185SAndres Beltran 
5040ba35fe9SAndrea Parri (Microsoft) 	/* Copy the RNDIS packet into nvchan->recv_buf */
5050ba35fe9SAndrea Parri (Microsoft) 	memcpy(rndis_pkt, data + RNDIS_HEADER_SIZE, sizeof(*rndis_pkt));
5060ba35fe9SAndrea Parri (Microsoft) 
50744144185SAndres Beltran 	/* Validate rndis_pkt offset */
50844144185SAndres Beltran 	if (rndis_pkt->data_offset >= data_buflen - RNDIS_HEADER_SIZE) {
50944144185SAndres Beltran 		netdev_err(ndev, "invalid rndis packet offset: %u\n",
51044144185SAndres Beltran 			   rndis_pkt->data_offset);
51144144185SAndres Beltran 		return NVSP_STAT_FAIL;
51244144185SAndres Beltran 	}
51344144185SAndres Beltran 
51495fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
51595fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
51695fa0405SHaiyang Zhang 
51744144185SAndres Beltran 	rpkt_len = data_buflen - RNDIS_HEADER_SIZE;
518dc54a08cSstephen hemminger 	data_buflen -= data_offset;
5194b8a8bc9SWei Yongjun 
5204b8a8bc9SWei Yongjun 	/*
5214b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
5224b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
5234b8a8bc9SWei Yongjun 	 */
524dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
52502400fceSStephen Hemminger 		netdev_err(ndev, "rndis message buffer "
5264b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
5274b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
528dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
52910082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
5304b8a8bc9SWei Yongjun 	}
5314b8a8bc9SWei Yongjun 
5320ba35fe9SAndrea Parri (Microsoft) 	vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0, sizeof(*vlan),
5330ba35fe9SAndrea Parri (Microsoft) 			     data);
534dc54a08cSstephen hemminger 
535505e3f00SAndrea Parri (Microsoft) 	csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0,
5360ba35fe9SAndrea Parri (Microsoft) 				  sizeof(*csum_info), data);
537c8e4eff4SHaiyang Zhang 
538505e3f00SAndrea Parri (Microsoft) 	hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0,
5390ba35fe9SAndrea Parri (Microsoft) 				  sizeof(*hash_info), data);
5401fac7ca4SStephen Hemminger 
541505e3f00SAndrea Parri (Microsoft) 	pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1,
5420ba35fe9SAndrea Parri (Microsoft) 				   sizeof(*pktinfo_id), data);
5433be9b5fdSHaiyang Zhang 
544c8e4eff4SHaiyang Zhang 	/* Identify RSC frags, drop erroneous packets */
545c8e4eff4SHaiyang Zhang 	if (pktinfo_id && (pktinfo_id->flag & RNDIS_PKTINFO_SUBALLOC)) {
546c8e4eff4SHaiyang Zhang 		if (pktinfo_id->flag & RNDIS_PKTINFO_1ST_FRAG)
547c8e4eff4SHaiyang Zhang 			nvchan->rsc.cnt = 0;
548c8e4eff4SHaiyang Zhang 		else if (nvchan->rsc.cnt == 0)
549c8e4eff4SHaiyang Zhang 			goto drop;
550c8e4eff4SHaiyang Zhang 
551c8e4eff4SHaiyang Zhang 		rsc_more = true;
552c8e4eff4SHaiyang Zhang 
553c8e4eff4SHaiyang Zhang 		if (pktinfo_id->flag & RNDIS_PKTINFO_LAST_FRAG)
554c8e4eff4SHaiyang Zhang 			rsc_more = false;
555c8e4eff4SHaiyang Zhang 
556c8e4eff4SHaiyang Zhang 		if (rsc_more && nvchan->rsc.is_last)
557c8e4eff4SHaiyang Zhang 			goto drop;
558c8e4eff4SHaiyang Zhang 	} else {
559c8e4eff4SHaiyang Zhang 		nvchan->rsc.cnt = 0;
560c8e4eff4SHaiyang Zhang 	}
561c8e4eff4SHaiyang Zhang 
562c8e4eff4SHaiyang Zhang 	if (unlikely(nvchan->rsc.cnt >= NVSP_RSC_MAX))
563c8e4eff4SHaiyang Zhang 		goto drop;
564c8e4eff4SHaiyang Zhang 
565c8e4eff4SHaiyang Zhang 	/* Put data into per channel structure.
566c8e4eff4SHaiyang Zhang 	 * Also, remove the rndis trailer padding from rndis packet message
5674b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
5684b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
5694b8a8bc9SWei Yongjun 	 */
5701fac7ca4SStephen Hemminger 	rsc_add_data(nvchan, vlan, csum_info, hash_info,
5710ba35fe9SAndrea Parri (Microsoft) 		     data + data_offset, rndis_pkt->data_len);
572c8e4eff4SHaiyang Zhang 
573c8e4eff4SHaiyang Zhang 	if (rsc_more)
574c8e4eff4SHaiyang Zhang 		return NVSP_STAT_SUCCESS;
575c8e4eff4SHaiyang Zhang 
576c8e4eff4SHaiyang Zhang 	ret = netvsc_recv_callback(ndev, nvdev, nvchan);
577c8e4eff4SHaiyang Zhang 	nvchan->rsc.cnt = 0;
578c8e4eff4SHaiyang Zhang 
579c8e4eff4SHaiyang Zhang 	return ret;
580c8e4eff4SHaiyang Zhang 
581c8e4eff4SHaiyang Zhang drop:
582c8e4eff4SHaiyang Zhang 	return NVSP_STAT_FAIL;
58395fa0405SHaiyang Zhang }
58495fa0405SHaiyang Zhang 
585dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
586dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
587c8e4eff4SHaiyang Zhang 			 struct netvsc_channel *nvchan,
588dc54a08cSstephen hemminger 			 void *data, u32 buflen)
58995fa0405SHaiyang Zhang {
5903d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
5910ba35fe9SAndrea Parri (Microsoft) 	struct rndis_message *rndis_msg = nvchan->recv_buf;
5920ba35fe9SAndrea Parri (Microsoft) 
5930ba35fe9SAndrea Parri (Microsoft) 	if (buflen < RNDIS_HEADER_SIZE) {
5940ba35fe9SAndrea Parri (Microsoft) 		netdev_err(ndev, "Invalid rndis_msg (buflen: %u)\n", buflen);
5950ba35fe9SAndrea Parri (Microsoft) 		return NVSP_STAT_FAIL;
5960ba35fe9SAndrea Parri (Microsoft) 	}
5970ba35fe9SAndrea Parri (Microsoft) 
5980ba35fe9SAndrea Parri (Microsoft) 	/* Copy the RNDIS msg header into nvchan->recv_buf */
5990ba35fe9SAndrea Parri (Microsoft) 	memcpy(rndis_msg, data, RNDIS_HEADER_SIZE);
60095fa0405SHaiyang Zhang 
60144144185SAndres Beltran 	/* Validate incoming rndis_message packet */
6020ba35fe9SAndrea Parri (Microsoft) 	if (rndis_msg->msg_len < RNDIS_HEADER_SIZE ||
60344144185SAndres Beltran 	    buflen < rndis_msg->msg_len) {
60444144185SAndres Beltran 		netdev_err(ndev, "Invalid rndis_msg (buflen: %u, msg_len: %u)\n",
60544144185SAndres Beltran 			   buflen, rndis_msg->msg_len);
60644144185SAndres Beltran 		return NVSP_STAT_FAIL;
60744144185SAndres Beltran 	}
60844144185SAndres Beltran 
609505e3f00SAndrea Parri (Microsoft) 	if (netif_msg_rx_status(net_device_ctx))
6100ba35fe9SAndrea Parri (Microsoft) 		dump_rndis_message(ndev, rndis_msg, data);
611505e3f00SAndrea Parri (Microsoft) 
612ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
61351491167SLinus Walleij 	case RNDIS_MSG_PACKET:
614c8e4eff4SHaiyang Zhang 		return rndis_filter_receive_data(ndev, net_dev, nvchan,
6150ba35fe9SAndrea Parri (Microsoft) 						 rndis_msg, data, buflen);
61651491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
61751491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
61851491167SLinus Walleij 	case RNDIS_MSG_SET_C:
61995fa0405SHaiyang Zhang 		/* completion msgs */
6200ba35fe9SAndrea Parri (Microsoft) 		rndis_filter_receive_response(ndev, net_dev, rndis_msg, data);
62195fa0405SHaiyang Zhang 		break;
62295fa0405SHaiyang Zhang 
62351491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
62495fa0405SHaiyang Zhang 		/* notification msgs */
6253946688eSAndrea Parri (Microsoft) 		netvsc_linkstatus_callback(ndev, rndis_msg, data, buflen);
62695fa0405SHaiyang Zhang 		break;
62795fa0405SHaiyang Zhang 	default:
62895fa0405SHaiyang Zhang 		netdev_err(ndev,
62995fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
630ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
631ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
6325c71dadbSHaiyang Zhang 		return NVSP_STAT_FAIL;
63395fa0405SHaiyang Zhang 	}
63495fa0405SHaiyang Zhang 
6355c71dadbSHaiyang Zhang 	return NVSP_STAT_SUCCESS;
63695fa0405SHaiyang Zhang }
63795fa0405SHaiyang Zhang 
638867047c4Sstephen hemminger static int rndis_filter_query_device(struct rndis_device *dev,
639867047c4Sstephen hemminger 				     struct netvsc_device *nvdev,
640867047c4Sstephen hemminger 				     u32 oid, void *result, u32 *result_size)
64195fa0405SHaiyang Zhang {
64295fa0405SHaiyang Zhang 	struct rndis_request *request;
64395fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
64495fa0405SHaiyang Zhang 	struct rndis_query_request *query;
64595fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
646505e3f00SAndrea Parri (Microsoft) 	u32 msg_len;
64795fa0405SHaiyang Zhang 	int ret = 0;
64895fa0405SHaiyang Zhang 
64995fa0405SHaiyang Zhang 	if (!result)
65095fa0405SHaiyang Zhang 		return -EINVAL;
65195fa0405SHaiyang Zhang 
65295fa0405SHaiyang Zhang 	*result_size = 0;
65351491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
65495fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
65595fa0405SHaiyang Zhang 	if (!request) {
65695fa0405SHaiyang Zhang 		ret = -ENOMEM;
65795fa0405SHaiyang Zhang 		goto cleanup;
65895fa0405SHaiyang Zhang 	}
65995fa0405SHaiyang Zhang 
66095fa0405SHaiyang Zhang 	/* Setup the rndis query */
66195fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
66295fa0405SHaiyang Zhang 	query->oid = oid;
66395fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
66495fa0405SHaiyang Zhang 	query->info_buflen = 0;
66595fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
66695fa0405SHaiyang Zhang 
66723312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
66823312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
66923312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
67023312a3bSstephen hemminger 		u8 ndis_rev;
67123312a3bSstephen hemminger 		size_t size;
67223312a3bSstephen hemminger 
67323312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
67423312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
67523312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
67623312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
67723312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
67823312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
67923312a3bSstephen hemminger 		} else {
68023312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
68123312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
68223312a3bSstephen hemminger 		}
68323312a3bSstephen hemminger 
68423312a3bSstephen hemminger 		request->request_msg.msg_len += size;
68523312a3bSstephen hemminger 		query->info_buflen = size;
68623312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
68723312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
68823312a3bSstephen hemminger 
68923312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
69023312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
69123312a3bSstephen hemminger 		hwcaps->header.size = size;
69223312a3bSstephen hemminger 
69323312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
6945b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
6955b54dac8SHaiyang Zhang 
6965b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
6975b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
6985b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
6995b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
7005b54dac8SHaiyang Zhang 						     query->info_buf_offset);
7015b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
7025b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
7035b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
7045b54dac8SHaiyang Zhang 	}
7055b54dac8SHaiyang Zhang 
70695fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
70795fa0405SHaiyang Zhang 	if (ret != 0)
70895fa0405SHaiyang Zhang 		goto cleanup;
70995fa0405SHaiyang Zhang 
7105362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
71195fa0405SHaiyang Zhang 
71295fa0405SHaiyang Zhang 	/* Copy the response back */
71395fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
714505e3f00SAndrea Parri (Microsoft) 	msg_len = request->response_msg.msg_len;
71595fa0405SHaiyang Zhang 
716505e3f00SAndrea Parri (Microsoft) 	/* Ensure the packet is big enough to access its fields */
717505e3f00SAndrea Parri (Microsoft) 	if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete)) {
718505e3f00SAndrea Parri (Microsoft) 		ret = -1;
719505e3f00SAndrea Parri (Microsoft) 		goto cleanup;
720505e3f00SAndrea Parri (Microsoft) 	}
721505e3f00SAndrea Parri (Microsoft) 
722505e3f00SAndrea Parri (Microsoft) 	if (query_complete->info_buflen > inresult_size ||
723505e3f00SAndrea Parri (Microsoft) 	    query_complete->info_buf_offset < sizeof(*query_complete) ||
724505e3f00SAndrea Parri (Microsoft) 	    msg_len - RNDIS_HEADER_SIZE < query_complete->info_buf_offset ||
725505e3f00SAndrea Parri (Microsoft) 	    msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset
726505e3f00SAndrea Parri (Microsoft) 			< query_complete->info_buflen) {
72795fa0405SHaiyang Zhang 		ret = -1;
72895fa0405SHaiyang Zhang 		goto cleanup;
72995fa0405SHaiyang Zhang 	}
73095fa0405SHaiyang Zhang 
73195fa0405SHaiyang Zhang 	memcpy(result,
73295fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
73395fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
73495fa0405SHaiyang Zhang 	       query_complete->info_buflen);
73595fa0405SHaiyang Zhang 
73695fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
73795fa0405SHaiyang Zhang 
73895fa0405SHaiyang Zhang cleanup:
73995fa0405SHaiyang Zhang 	if (request)
74095fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
74195fa0405SHaiyang Zhang 
74295fa0405SHaiyang Zhang 	return ret;
74395fa0405SHaiyang Zhang }
74495fa0405SHaiyang Zhang 
74523312a3bSstephen hemminger /* Get the hardware offload capabilities */
74623312a3bSstephen hemminger static int
747867047c4Sstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device,
748867047c4Sstephen hemminger 		   struct ndis_offload *caps)
74923312a3bSstephen hemminger {
75023312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
75123312a3bSstephen hemminger 	int ret;
75223312a3bSstephen hemminger 
75323312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
75423312a3bSstephen hemminger 
755867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
75623312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
75723312a3bSstephen hemminger 					caps, &caps_len);
75823312a3bSstephen hemminger 	if (ret)
75923312a3bSstephen hemminger 		return ret;
76023312a3bSstephen hemminger 
76123312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
76223312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
76323312a3bSstephen hemminger 			    caps->header.type);
76423312a3bSstephen hemminger 		return -EINVAL;
76523312a3bSstephen hemminger 	}
76623312a3bSstephen hemminger 
76723312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
76823312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
76923312a3bSstephen hemminger 			    caps->header.revision);
77023312a3bSstephen hemminger 		return -EINVAL;
77123312a3bSstephen hemminger 	}
77223312a3bSstephen hemminger 
77323312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
77423312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
77523312a3bSstephen hemminger 		netdev_warn(dev->ndev,
77623312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
77723312a3bSstephen hemminger 			    caps->header.size, caps_len);
77823312a3bSstephen hemminger 		return -EINVAL;
77923312a3bSstephen hemminger 	}
78023312a3bSstephen hemminger 
78123312a3bSstephen hemminger 	return 0;
78223312a3bSstephen hemminger }
78323312a3bSstephen hemminger 
784867047c4Sstephen hemminger static int rndis_filter_query_device_mac(struct rndis_device *dev,
785867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
78695fa0405SHaiyang Zhang {
78795fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
78895fa0405SHaiyang Zhang 
789867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
79095fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
79195fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
79295fa0405SHaiyang Zhang }
79395fa0405SHaiyang Zhang 
7941ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
7951ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
7961ce09e89SHaiyang Zhang 
797867047c4Sstephen hemminger int rndis_filter_set_device_mac(struct netvsc_device *nvdev,
798867047c4Sstephen hemminger 				const char *mac)
7991ce09e89SHaiyang Zhang {
8001ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
8011ce09e89SHaiyang Zhang 	struct rndis_request *request;
8021ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
8031ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
8041ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
8051ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
8061ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
8071ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
8081ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
809999028ccSNicholas Mc Guire 	int ret;
8101ce09e89SHaiyang Zhang 
8111ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
8121ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
8131ce09e89SHaiyang Zhang 	if (!request)
8141ce09e89SHaiyang Zhang 		return -ENOMEM;
8151ce09e89SHaiyang Zhang 
8161ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
8171ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
8181ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
8191ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
8201ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
8211ce09e89SHaiyang Zhang 
8221ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
8231ce09e89SHaiyang Zhang 		set->info_buf_offset);
8241ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
8251ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
8261ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
8271ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
8281ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
8291ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
8301ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
8311ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
8321ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
8331ce09e89SHaiyang Zhang 
8341ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
8351ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
8361ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
8371ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
8381ce09e89SHaiyang Zhang 	if (ret < 0)
8391ce09e89SHaiyang Zhang 		goto cleanup;
8401ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
8411ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
8421ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
8431ce09e89SHaiyang Zhang 	if (ret < 0)
8441ce09e89SHaiyang Zhang 		goto cleanup;
8451ce09e89SHaiyang Zhang 
8461ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
8471ce09e89SHaiyang Zhang 	if (ret != 0)
8481ce09e89SHaiyang Zhang 		goto cleanup;
8491ce09e89SHaiyang Zhang 
8505362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
8515362855aSVitaly Kuznetsov 
8521ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
853867047c4Sstephen hemminger 	if (set_complete->status != RNDIS_STATUS_SUCCESS)
854867047c4Sstephen hemminger 		ret = -EIO;
8551ce09e89SHaiyang Zhang 
8561ce09e89SHaiyang Zhang cleanup:
8571ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
8581ce09e89SHaiyang Zhang 	return ret;
8591ce09e89SHaiyang Zhang }
8601ce09e89SHaiyang Zhang 
861d6792a5aSHaiyang Zhang int
862426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
8639749fed5Sstephen hemminger 				struct netvsc_device *nvdev,
8644a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
8654a0e70aeSKY Srinivasan {
8664a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
8674a0e70aeSKY Srinivasan 	struct rndis_request *request;
8684a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
8694a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
8704a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
8714a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
872999028ccSNicholas Mc Guire 	int ret;
873af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
874af9893a3SKY Srinivasan 
875af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
876af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
877af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
878af9893a3SKY Srinivasan 		 * UDP checksum offload.
879af9893a3SKY Srinivasan 		 */
880af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
881af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
882af9893a3SKY Srinivasan 	}
8834a0e70aeSKY Srinivasan 
8844a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
8854a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
8864a0e70aeSKY Srinivasan 	if (!request)
8874a0e70aeSKY Srinivasan 		return -ENOMEM;
8884a0e70aeSKY Srinivasan 
8894a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
8904a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
8914a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
8924a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
8934a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
8944a0e70aeSKY Srinivasan 
8954a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
8964a0e70aeSKY Srinivasan 				set->info_buf_offset);
8974a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
8984a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
8994a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
9004a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
9014a0e70aeSKY Srinivasan 
9024a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
9034a0e70aeSKY Srinivasan 	if (ret != 0)
9044a0e70aeSKY Srinivasan 		goto cleanup;
9054a0e70aeSKY Srinivasan 
9065362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
9074a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
9084a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
909af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
9104a0e70aeSKY Srinivasan 			   set_complete->status);
9114a0e70aeSKY Srinivasan 		ret = -EINVAL;
9124a0e70aeSKY Srinivasan 	}
9134a0e70aeSKY Srinivasan 
9144a0e70aeSKY Srinivasan cleanup:
9154a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
9164a0e70aeSKY Srinivasan 	return ret;
9174a0e70aeSKY Srinivasan }
9181ce09e89SHaiyang Zhang 
919b4a10c75SHaiyang Zhang static int rndis_set_rss_param_msg(struct rndis_device *rdev,
920b4a10c75SHaiyang Zhang 				   const u8 *rss_key, u16 flag)
9215b54dac8SHaiyang Zhang {
9223d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
923b0689faaSHaiyang Zhang 	struct net_device_context *ndc = netdev_priv(ndev);
9245b54dac8SHaiyang Zhang 	struct rndis_request *request;
9255b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
9265b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
9275b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
928962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
9295b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
9305b54dac8SHaiyang Zhang 	u32 *itab;
9315b54dac8SHaiyang Zhang 	u8 *keyp;
932999028ccSNicholas Mc Guire 	int i, ret;
9335b54dac8SHaiyang Zhang 
9345b54dac8SHaiyang Zhang 	request = get_rndis_request(
9355b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
9365b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
9375b54dac8SHaiyang Zhang 	if (!request)
9385b54dac8SHaiyang Zhang 		return -ENOMEM;
9395b54dac8SHaiyang Zhang 
9405b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
9415b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
9425b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
9435b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
9445b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
9455b54dac8SHaiyang Zhang 
9465b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
9475b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
9485b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
9495b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
950b4a10c75SHaiyang Zhang 	rssp->flag = flag;
9515b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
9524c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
9534c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
9545b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
9555b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
956962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
957bfcbcb67SStephen Hemminger 	rssp->hashkey_offset = rssp->indirect_taboffset +
9585b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
9595b54dac8SHaiyang Zhang 
9605b54dac8SHaiyang Zhang 	/* Set indirection table entries */
9615b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
9625b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
963b0689faaSHaiyang Zhang 		itab[i] = ndc->rx_table[i];
9645b54dac8SHaiyang Zhang 
9655b54dac8SHaiyang Zhang 	/* Set hask key values */
966bfcbcb67SStephen Hemminger 	keyp = (u8 *)((unsigned long)rssp + rssp->hashkey_offset);
967962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
9685b54dac8SHaiyang Zhang 
9695b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
9705b54dac8SHaiyang Zhang 	if (ret != 0)
9715b54dac8SHaiyang Zhang 		goto cleanup;
9725b54dac8SHaiyang Zhang 
9735362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
9745b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
975b4a10c75SHaiyang Zhang 	if (set_complete->status == RNDIS_STATUS_SUCCESS) {
976b4a10c75SHaiyang Zhang 		if (!(flag & NDIS_RSS_PARAM_FLAG_DISABLE_RSS) &&
977b4a10c75SHaiyang Zhang 		    !(flag & NDIS_RSS_PARAM_FLAG_HASH_KEY_UNCHANGED))
978962f3feeSstephen hemminger 			memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
979b4a10c75SHaiyang Zhang 
980b4a10c75SHaiyang Zhang 	} else {
9815b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
9825b54dac8SHaiyang Zhang 			   set_complete->status);
9835b54dac8SHaiyang Zhang 		ret = -EINVAL;
9845b54dac8SHaiyang Zhang 	}
9855b54dac8SHaiyang Zhang 
9865b54dac8SHaiyang Zhang cleanup:
9875b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
9885b54dac8SHaiyang Zhang 	return ret;
9895b54dac8SHaiyang Zhang }
9905b54dac8SHaiyang Zhang 
991b4a10c75SHaiyang Zhang int rndis_filter_set_rss_param(struct rndis_device *rdev,
992b4a10c75SHaiyang Zhang 			       const u8 *rss_key)
993b4a10c75SHaiyang Zhang {
994b4a10c75SHaiyang Zhang 	/* Disable RSS before change */
995b4a10c75SHaiyang Zhang 	rndis_set_rss_param_msg(rdev, rss_key,
996b4a10c75SHaiyang Zhang 				NDIS_RSS_PARAM_FLAG_DISABLE_RSS);
997b4a10c75SHaiyang Zhang 
998b4a10c75SHaiyang Zhang 	return rndis_set_rss_param_msg(rdev, rss_key, 0);
999b4a10c75SHaiyang Zhang }
1000b4a10c75SHaiyang Zhang 
1001867047c4Sstephen hemminger static int rndis_filter_query_device_link_status(struct rndis_device *dev,
1002867047c4Sstephen hemminger 						 struct netvsc_device *net_device)
100395fa0405SHaiyang Zhang {
100495fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
100595fa0405SHaiyang Zhang 	u32 link_status;
100695fa0405SHaiyang Zhang 
1007867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
100895fa0405SHaiyang Zhang 					 RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
100995fa0405SHaiyang Zhang 					 &link_status, &size);
101095fa0405SHaiyang Zhang }
101195fa0405SHaiyang Zhang 
1012867047c4Sstephen hemminger static int rndis_filter_query_link_speed(struct rndis_device *dev,
1013867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
1014b37879e6SHaiyang Zhang {
1015b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
1016b37879e6SHaiyang Zhang 	u32 link_speed;
1017b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
1018b37879e6SHaiyang Zhang 	int ret;
1019b37879e6SHaiyang Zhang 
1020867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
1021867047c4Sstephen hemminger 					RNDIS_OID_GEN_LINK_SPEED,
1022b37879e6SHaiyang Zhang 					&link_speed, &size);
1023b37879e6SHaiyang Zhang 
1024b37879e6SHaiyang Zhang 	if (!ret) {
1025b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
1026b37879e6SHaiyang Zhang 
1027b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
1028b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
1029b37879e6SHaiyang Zhang 		 */
1030b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
1031b37879e6SHaiyang Zhang 	}
1032b37879e6SHaiyang Zhang 
1033b37879e6SHaiyang Zhang 	return ret;
1034b37879e6SHaiyang Zhang }
1035b37879e6SHaiyang Zhang 
10364f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev,
10374f19c0d8Sstephen hemminger 					  u32 new_filter)
103895fa0405SHaiyang Zhang {
103995fa0405SHaiyang Zhang 	struct rndis_request *request;
104095fa0405SHaiyang Zhang 	struct rndis_set_request *set;
1041999028ccSNicholas Mc Guire 	int ret;
104295fa0405SHaiyang Zhang 
10437eeb4a6eSStephen Hemminger 	if (dev->filter == new_filter)
10447eeb4a6eSStephen Hemminger 		return 0;
10457eeb4a6eSStephen Hemminger 
104651491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
104795fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
104895fa0405SHaiyang Zhang 			sizeof(u32));
1049ce12b810Sstephen hemminger 	if (!request)
1050ce12b810Sstephen hemminger 		return -ENOMEM;
1051ce12b810Sstephen hemminger 
105295fa0405SHaiyang Zhang 	/* Setup the rndis set */
105395fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
105495fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
105595fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
1056f2fcffe3SKees Cook 	set->info_buf_offset = offsetof(typeof(*set), info_buf);
1057f2fcffe3SKees Cook 	memcpy(set->info_buf, &new_filter, sizeof(u32));
105895fa0405SHaiyang Zhang 
105995fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
10607eeb4a6eSStephen Hemminger 	if (ret == 0) {
10615362855aSVitaly Kuznetsov 		wait_for_completion(&request->wait_event);
10627eeb4a6eSStephen Hemminger 		dev->filter = new_filter;
10637eeb4a6eSStephen Hemminger 	}
106495fa0405SHaiyang Zhang 
106595fa0405SHaiyang Zhang 	put_rndis_request(dev, request);
1066ce12b810Sstephen hemminger 
106795fa0405SHaiyang Zhang 	return ret;
106895fa0405SHaiyang Zhang }
106995fa0405SHaiyang Zhang 
10704f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w)
10714f19c0d8Sstephen hemminger {
10724f19c0d8Sstephen hemminger 	struct rndis_device *rdev
10734f19c0d8Sstephen hemminger 		= container_of(w, struct rndis_device, mcast_work);
1074009f766cSStephen Hemminger 	u32 filter = NDIS_PACKET_TYPE_DIRECTED;
1075009f766cSStephen Hemminger 	unsigned int flags = rdev->ndev->flags;
10764f19c0d8Sstephen hemminger 
1077009f766cSStephen Hemminger 	if (flags & IFF_PROMISC) {
1078009f766cSStephen Hemminger 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
1079009f766cSStephen Hemminger 	} else {
1080f03dbb06SStephen Hemminger 		if (!netdev_mc_empty(rdev->ndev) || (flags & IFF_ALLMULTI))
1081de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
1082009f766cSStephen Hemminger 		if (flags & IFF_BROADCAST)
1083de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_BROADCAST;
1084009f766cSStephen Hemminger 	}
1085009f766cSStephen Hemminger 
1086009f766cSStephen Hemminger 	rndis_filter_set_packet_filter(rdev, filter);
10874f19c0d8Sstephen hemminger }
10884f19c0d8Sstephen hemminger 
10894f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev)
10904f19c0d8Sstephen hemminger {
10914f19c0d8Sstephen hemminger 	struct rndis_device *rdev = nvdev->extension;
10924f19c0d8Sstephen hemminger 
10934f19c0d8Sstephen hemminger 	schedule_work(&rdev->mcast_work);
10944f19c0d8Sstephen hemminger }
10954f19c0d8Sstephen hemminger 
1096867047c4Sstephen hemminger static int rndis_filter_init_device(struct rndis_device *dev,
1097867047c4Sstephen hemminger 				    struct netvsc_device *nvdev)
109895fa0405SHaiyang Zhang {
109995fa0405SHaiyang Zhang 	struct rndis_request *request;
110095fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
110195fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
110295fa0405SHaiyang Zhang 	u32 status;
1103999028ccSNicholas Mc Guire 	int ret;
110495fa0405SHaiyang Zhang 
110551491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
110695fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
110795fa0405SHaiyang Zhang 	if (!request) {
110895fa0405SHaiyang Zhang 		ret = -ENOMEM;
110995fa0405SHaiyang Zhang 		goto cleanup;
111095fa0405SHaiyang Zhang 	}
111195fa0405SHaiyang Zhang 
111295fa0405SHaiyang Zhang 	/* Setup the rndis set */
111395fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
111495fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
111595fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
1116fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
111795fa0405SHaiyang Zhang 
111895fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
111995fa0405SHaiyang Zhang 
112095fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
112195fa0405SHaiyang Zhang 	if (ret != 0) {
112295fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
112395fa0405SHaiyang Zhang 		goto cleanup;
112495fa0405SHaiyang Zhang 	}
112595fa0405SHaiyang Zhang 
11265362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
112795fa0405SHaiyang Zhang 
112895fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
112995fa0405SHaiyang Zhang 	status = init_complete->status;
113095fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
113195fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
11327c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
11337c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
113495fa0405SHaiyang Zhang 		ret = 0;
113595fa0405SHaiyang Zhang 	} else {
113695fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
113795fa0405SHaiyang Zhang 		ret = -EINVAL;
113895fa0405SHaiyang Zhang 	}
113995fa0405SHaiyang Zhang 
114095fa0405SHaiyang Zhang cleanup:
114195fa0405SHaiyang Zhang 	if (request)
114295fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
114395fa0405SHaiyang Zhang 
114495fa0405SHaiyang Zhang 	return ret;
114595fa0405SHaiyang Zhang }
114695fa0405SHaiyang Zhang 
114746b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev)
114846b4f7f5Sstephen hemminger {
114946b4f7f5Sstephen hemminger 	int i;
115046b4f7f5Sstephen hemminger 
115146b4f7f5Sstephen hemminger 	for (i = 0; i < nvdev->num_chn; i++) {
115246b4f7f5Sstephen hemminger 		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
115346b4f7f5Sstephen hemminger 
11547426b1a5Sstephen hemminger 		if (nvchan->mrc.first != nvchan->mrc.next)
11557426b1a5Sstephen hemminger 			return false;
11567426b1a5Sstephen hemminger 
115746b4f7f5Sstephen hemminger 		if (atomic_read(&nvchan->queue_sends) > 0)
115846b4f7f5Sstephen hemminger 			return false;
115946b4f7f5Sstephen hemminger 	}
116046b4f7f5Sstephen hemminger 
116146b4f7f5Sstephen hemminger 	return true;
116246b4f7f5Sstephen hemminger }
116346b4f7f5Sstephen hemminger 
11640e96460eSStephen Hemminger static void rndis_filter_halt_device(struct netvsc_device *nvdev,
11650e96460eSStephen Hemminger 				     struct rndis_device *dev)
116695fa0405SHaiyang Zhang {
116795fa0405SHaiyang Zhang 	struct rndis_request *request;
116895fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
116995fa0405SHaiyang Zhang 
117095fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
117151491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
117295fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
117395fa0405SHaiyang Zhang 	if (!request)
117495fa0405SHaiyang Zhang 		goto cleanup;
117595fa0405SHaiyang Zhang 
117695fa0405SHaiyang Zhang 	/* Setup the rndis set */
117795fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
117895fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
117995fa0405SHaiyang Zhang 
118095fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
118195fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
118295fa0405SHaiyang Zhang 
118395fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
118495fa0405SHaiyang Zhang 
118595fa0405SHaiyang Zhang cleanup:
1186ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
118700ecfb3bSstephen hemminger 
118800ecfb3bSstephen hemminger 	/* Force flag to be ordered before waiting */
118900ecfb3bSstephen hemminger 	wmb();
1190ae9e63bbSHaiyang Zhang 
1191ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
119246b4f7f5Sstephen hemminger 	wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
1193ae9e63bbSHaiyang Zhang 
119495fa0405SHaiyang Zhang 	if (request)
119595fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
119695fa0405SHaiyang Zhang }
119795fa0405SHaiyang Zhang 
119895fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
119995fa0405SHaiyang Zhang {
120095fa0405SHaiyang Zhang 	int ret;
120195fa0405SHaiyang Zhang 
120295fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
120395fa0405SHaiyang Zhang 		return 0;
120495fa0405SHaiyang Zhang 
120595fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
120695fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
120795fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
120895fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
120995fa0405SHaiyang Zhang 	if (ret == 0)
121095fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
121195fa0405SHaiyang Zhang 
121295fa0405SHaiyang Zhang 	return ret;
121395fa0405SHaiyang Zhang }
121495fa0405SHaiyang Zhang 
121595fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
121695fa0405SHaiyang Zhang {
121795fa0405SHaiyang Zhang 	int ret;
121895fa0405SHaiyang Zhang 
121995fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
122095fa0405SHaiyang Zhang 		return 0;
122195fa0405SHaiyang Zhang 
12224f19c0d8Sstephen hemminger 	/* Make sure rndis_set_multicast doesn't re-enable filter! */
12234f19c0d8Sstephen hemminger 	cancel_work_sync(&dev->mcast_work);
12244f19c0d8Sstephen hemminger 
122595fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
1226c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
1227c3582a2cSHaiyang Zhang 		ret = 0;
1228c3582a2cSHaiyang Zhang 
122995fa0405SHaiyang Zhang 	if (ret == 0)
123095fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
123195fa0405SHaiyang Zhang 
123295fa0405SHaiyang Zhang 	return ret;
123395fa0405SHaiyang Zhang }
123495fa0405SHaiyang Zhang 
12355b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
12365b54dac8SHaiyang Zhang {
12373d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
12383d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
1239867047c4Sstephen hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
1240867047c4Sstephen hemminger 	struct netvsc_device *nvscdev;
12415b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
12426de38af6Sstephen hemminger 	struct netvsc_channel *nvchan;
12436de38af6Sstephen hemminger 	int ret;
12445b54dac8SHaiyang Zhang 
1245867047c4Sstephen hemminger 	/* This is safe because this callback only happens when
1246867047c4Sstephen hemminger 	 * new device is being setup and waiting on the channel_init_wait.
1247867047c4Sstephen hemminger 	 */
1248867047c4Sstephen hemminger 	nvscdev = rcu_dereference_raw(ndev_ctx->nvdev);
1249867047c4Sstephen hemminger 	if (!nvscdev || chn_index >= nvscdev->num_chn)
12505b54dac8SHaiyang Zhang 		return;
12515b54dac8SHaiyang Zhang 
12526de38af6Sstephen hemminger 	nvchan = nvscdev->chan_table + chn_index;
12536de38af6Sstephen hemminger 
1254b1dd90ceSK. Y. Srinivasan 	/* Because the device uses NAPI, all the interrupt batching and
1255b1dd90ceSK. Y. Srinivasan 	 * control is done via Net softirq, not the channel handling
1256b1dd90ceSK. Y. Srinivasan 	 */
1257b1dd90ceSK. Y. Srinivasan 	set_channel_read_mode(new_sc, HV_CALL_ISR);
1258b1dd90ceSK. Y. Srinivasan 
1259bffb1842SK. Y. Srinivasan 	/* Set the channel before opening.*/
1260bffb1842SK. Y. Srinivasan 	nvchan->channel = new_sc;
1261bffb1842SK. Y. Srinivasan 
1262bf5fd8caSAndrea Parri (Microsoft) 	new_sc->next_request_id_callback = vmbus_next_request_id;
1263bf5fd8caSAndrea Parri (Microsoft) 	new_sc->request_addr_callback = vmbus_request_addr;
12644d18fcc9SAndres Beltran 	new_sc->rqstor_size = netvsc_rqstor_size(netvsc_ring_bytes);
1265adae1e93SAndres Beltran 	new_sc->max_pkt_size = NETVSC_MAX_PKT_SIZE;
1266adae1e93SAndres Beltran 
1267a7f99d0fSStephen Hemminger 	ret = vmbus_open(new_sc, netvsc_ring_bytes,
1268a7f99d0fSStephen Hemminger 			 netvsc_ring_bytes, NULL, 0,
12696de38af6Sstephen hemminger 			 netvsc_channel_cb, nvchan);
127076bb5db5Sstephen hemminger 	if (ret == 0)
12716de38af6Sstephen hemminger 		napi_enable(&nvchan->napi);
127276bb5db5Sstephen hemminger 	else
12738195b139SStephen Hemminger 		netdev_notice(ndev, "sub channel open failed: %d\n", ret);
127415a863bfSstephen hemminger 
12758f2bb1deSStephen Hemminger 	if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn)
1276732e4985Sstephen hemminger 		wake_up(&nvscdev->subchan_open);
12775b54dac8SHaiyang Zhang }
12785b54dac8SHaiyang Zhang 
12798195b139SStephen Hemminger /* Open sub-channels after completing the handling of the device probe.
12808195b139SStephen Hemminger  * This breaks overlap of processing the host message for the
12818195b139SStephen Hemminger  * new primary channel with the initialization of sub-channels.
12828195b139SStephen Hemminger  */
128317d91256SHaiyang Zhang int rndis_set_subchannel(struct net_device *ndev,
128417d91256SHaiyang Zhang 			 struct netvsc_device *nvdev,
128517d91256SHaiyang Zhang 			 struct netvsc_device_info *dev_info)
12868195b139SStephen Hemminger {
12878195b139SStephen Hemminger 	struct nvsp_message *init_packet = &nvdev->channel_init_pkt;
12883ffe64f1SStephen Hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
12893ffe64f1SStephen Hemminger 	struct hv_device *hv_dev = ndev_ctx->device_ctx;
12903ffe64f1SStephen Hemminger 	struct rndis_device *rdev = nvdev->extension;
12918195b139SStephen Hemminger 	int i, ret;
12928195b139SStephen Hemminger 
12933ffe64f1SStephen Hemminger 	ASSERT_RTNL();
12948195b139SStephen Hemminger 
12958195b139SStephen Hemminger 	memset(init_packet, 0, sizeof(struct nvsp_message));
12968195b139SStephen Hemminger 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
12978195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
12988195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
12998195b139SStephen Hemminger 						nvdev->num_chn - 1;
1300ec966381SStephen Hemminger 	trace_nvsp_send(ndev, init_packet);
1301ec966381SStephen Hemminger 
13028195b139SStephen Hemminger 	ret = vmbus_sendpacket(hv_dev->channel, init_packet,
13038195b139SStephen Hemminger 			       sizeof(struct nvsp_message),
13048195b139SStephen Hemminger 			       (unsigned long)init_packet,
13058195b139SStephen Hemminger 			       VM_PKT_DATA_INBAND,
13068195b139SStephen Hemminger 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
13078195b139SStephen Hemminger 	if (ret) {
13088195b139SStephen Hemminger 		netdev_err(ndev, "sub channel allocate send failed: %d\n", ret);
13093ffe64f1SStephen Hemminger 		return ret;
13108195b139SStephen Hemminger 	}
13118195b139SStephen Hemminger 
13128195b139SStephen Hemminger 	wait_for_completion(&nvdev->channel_init_wait);
13138195b139SStephen Hemminger 	if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
13148195b139SStephen Hemminger 		netdev_err(ndev, "sub channel request failed\n");
13153ffe64f1SStephen Hemminger 		return -EIO;
13168195b139SStephen Hemminger 	}
13178195b139SStephen Hemminger 
1318206ad34dSAndrea Parri (Microsoft) 	/* Check that number of allocated sub channel is within the expected range */
1319206ad34dSAndrea Parri (Microsoft) 	if (init_packet->msg.v5_msg.subchn_comp.num_subchannels > nvdev->num_chn - 1) {
1320206ad34dSAndrea Parri (Microsoft) 		netdev_err(ndev, "invalid number of allocated sub channel\n");
1321206ad34dSAndrea Parri (Microsoft) 		return -EINVAL;
1322206ad34dSAndrea Parri (Microsoft) 	}
13238195b139SStephen Hemminger 	nvdev->num_chn = 1 +
13248195b139SStephen Hemminger 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
13258195b139SStephen Hemminger 
13268195b139SStephen Hemminger 	/* wait for all sub channels to open */
13278195b139SStephen Hemminger 	wait_event(nvdev->subchan_open,
13288195b139SStephen Hemminger 		   atomic_read(&nvdev->open_chn) == nvdev->num_chn);
13298195b139SStephen Hemminger 
1330c39ea5cbSHaiyang Zhang 	for (i = 0; i < VRSS_SEND_TAB_SIZE; i++)
1331c39ea5cbSHaiyang Zhang 		ndev_ctx->tx_table[i] = i % nvdev->num_chn;
1332c39ea5cbSHaiyang Zhang 
133352d3b494SAdrian Vladu 	/* ignore failures from setting rss parameters, still have channels */
133417d91256SHaiyang Zhang 	if (dev_info)
133517d91256SHaiyang Zhang 		rndis_filter_set_rss_param(rdev, dev_info->rss_key);
133617d91256SHaiyang Zhang 	else
13378195b139SStephen Hemminger 		rndis_filter_set_rss_param(rdev, netvsc_hash_key);
13388195b139SStephen Hemminger 
13398195b139SStephen Hemminger 	netif_set_real_num_tx_queues(ndev, nvdev->num_chn);
13408195b139SStephen Hemminger 	netif_set_real_num_rx_queues(ndev, nvdev->num_chn);
13418195b139SStephen Hemminger 
13423ffe64f1SStephen Hemminger 	return 0;
13438195b139SStephen Hemminger }
13448195b139SStephen Hemminger 
1345aefd80e8SVitaly Kuznetsov static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device,
1346aefd80e8SVitaly Kuznetsov 				   struct netvsc_device *nvdev)
134795fa0405SHaiyang Zhang {
1348aefd80e8SVitaly Kuznetsov 	struct net_device *net = rndis_device->ndev;
13493d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
135023312a3bSstephen hemminger 	struct ndis_offload hwcaps;
13514a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
135223312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
1353aefd80e8SVitaly Kuznetsov 	int ret;
135495fa0405SHaiyang Zhang 
135523312a3bSstephen hemminger 	/* Find HW offload capabilities */
1356aefd80e8SVitaly Kuznetsov 	ret = rndis_query_hwcaps(rndis_device, nvdev, &hwcaps);
13579749fed5Sstephen hemminger 	if (ret != 0)
1358aefd80e8SVitaly Kuznetsov 		return ret;
135923312a3bSstephen hemminger 
136023312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
13614a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
136223312a3bSstephen hemminger 
136323312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
136423312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
136523312a3bSstephen hemminger 
1366aefd80e8SVitaly Kuznetsov 	/* Reset previously set hw_features flags */
1367aefd80e8SVitaly Kuznetsov 	net->hw_features &= ~NETVSC_SUPPORTED_HW_FEATURES;
1368aefd80e8SVitaly Kuznetsov 	net_device_ctx->tx_checksum_mask = 0;
1369aefd80e8SVitaly Kuznetsov 
137023312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
1371aefd80e8SVitaly Kuznetsov 	net->hw_features |= NETIF_F_RXCSUM;
1372b441f795SHaiyang Zhang 	net->hw_features |= NETIF_F_SG;
13736f3aeb1bSStephen Hemminger 	net->hw_features |= NETIF_F_RXHASH;
137423312a3bSstephen hemminger 
137523312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
137623312a3bSstephen hemminger 		/* Can checksum TCP */
137723312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
137823312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
137923312a3bSstephen hemminger 
13804a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
138123312a3bSstephen hemminger 
138223312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
13834a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
138423312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
138523312a3bSstephen hemminger 
138623312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
138723312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
138823312a3bSstephen hemminger 		}
138923312a3bSstephen hemminger 
139023312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
139123312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
139223312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
139323312a3bSstephen hemminger 		}
139423312a3bSstephen hemminger 	}
139523312a3bSstephen hemminger 
139623312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
139723312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
139823312a3bSstephen hemminger 
139923312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
140023312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
140123312a3bSstephen hemminger 
140223312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
140323312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
140423312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
140523312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
140623312a3bSstephen hemminger 
140723312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
140823312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
140923312a3bSstephen hemminger 		}
141023312a3bSstephen hemminger 
141123312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
141223312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
141323312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
141423312a3bSstephen hemminger 		}
141523312a3bSstephen hemminger 	}
141623312a3bSstephen hemminger 
1417c8e4eff4SHaiyang Zhang 	if (hwcaps.rsc.ip4 && hwcaps.rsc.ip6) {
1418c8e4eff4SHaiyang Zhang 		net->hw_features |= NETIF_F_LRO;
1419c8e4eff4SHaiyang Zhang 
1420d6792a5aSHaiyang Zhang 		if (net->features & NETIF_F_LRO) {
1421c8e4eff4SHaiyang Zhang 			offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
1422c8e4eff4SHaiyang Zhang 			offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
1423d6792a5aSHaiyang Zhang 		} else {
1424d6792a5aSHaiyang Zhang 			offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED;
1425d6792a5aSHaiyang Zhang 			offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED;
1426d6792a5aSHaiyang Zhang 		}
1427c8e4eff4SHaiyang Zhang 	}
1428c8e4eff4SHaiyang Zhang 
1429aefd80e8SVitaly Kuznetsov 	/* In case some hw_features disappeared we need to remove them from
1430aefd80e8SVitaly Kuznetsov 	 * net->features list as they're no longer supported.
1431aefd80e8SVitaly Kuznetsov 	 */
1432aefd80e8SVitaly Kuznetsov 	net->features &= ~NETVSC_SUPPORTED_HW_FEATURES | net->hw_features;
1433aefd80e8SVitaly Kuznetsov 
1434*ee8b7a11SJakub Kicinski 	netif_set_tso_max_size(net, gso_max_size);
14354a0e70aeSKY Srinivasan 
1436aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_set_offload_params(net, nvdev, &offloads);
1437aefd80e8SVitaly Kuznetsov 
1438aefd80e8SVitaly Kuznetsov 	return ret;
1439aefd80e8SVitaly Kuznetsov }
1440aefd80e8SVitaly Kuznetsov 
14410fe554a4SStephen Hemminger static void rndis_get_friendly_name(struct net_device *net,
14420fe554a4SStephen Hemminger 				    struct rndis_device *rndis_device,
14430fe554a4SStephen Hemminger 				    struct netvsc_device *net_device)
14440fe554a4SStephen Hemminger {
14450fe554a4SStephen Hemminger 	ucs2_char_t wname[256];
14460fe554a4SStephen Hemminger 	unsigned long len;
14470fe554a4SStephen Hemminger 	u8 ifalias[256];
14480fe554a4SStephen Hemminger 	u32 size;
14490fe554a4SStephen Hemminger 
14500fe554a4SStephen Hemminger 	size = sizeof(wname);
14510fe554a4SStephen Hemminger 	if (rndis_filter_query_device(rndis_device, net_device,
14520fe554a4SStephen Hemminger 				      RNDIS_OID_GEN_FRIENDLY_NAME,
14530fe554a4SStephen Hemminger 				      wname, &size) != 0)
1454d97cde6aSStephen Hemminger 		return;	/* ignore if host does not support */
1455d97cde6aSStephen Hemminger 
1456d97cde6aSStephen Hemminger 	if (size == 0)
1457d97cde6aSStephen Hemminger 		return;	/* name not set */
14580fe554a4SStephen Hemminger 
14590fe554a4SStephen Hemminger 	/* Convert Windows Unicode string to UTF-8 */
14600fe554a4SStephen Hemminger 	len = ucs2_as_utf8(ifalias, wname, sizeof(ifalias));
14610fe554a4SStephen Hemminger 
14620fe554a4SStephen Hemminger 	/* ignore the default value from host */
14630fe554a4SStephen Hemminger 	if (strcmp(ifalias, "Network Adapter") != 0)
14640fe554a4SStephen Hemminger 		dev_set_alias(net, ifalias, len);
14650fe554a4SStephen Hemminger }
14660fe554a4SStephen Hemminger 
1467aefd80e8SVitaly Kuznetsov struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
1468aefd80e8SVitaly Kuznetsov 				      struct netvsc_device_info *device_info)
1469aefd80e8SVitaly Kuznetsov {
1470aefd80e8SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
1471b0689faaSHaiyang Zhang 	struct net_device_context *ndc = netdev_priv(net);
1472aefd80e8SVitaly Kuznetsov 	struct netvsc_device *net_device;
1473aefd80e8SVitaly Kuznetsov 	struct rndis_device *rndis_device;
1474aefd80e8SVitaly Kuznetsov 	struct ndis_recv_scale_cap rsscap;
1475aefd80e8SVitaly Kuznetsov 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
1476aefd80e8SVitaly Kuznetsov 	u32 mtu, size;
1477aefd80e8SVitaly Kuznetsov 	u32 num_possible_rss_qs;
1478aefd80e8SVitaly Kuznetsov 	int i, ret;
1479aefd80e8SVitaly Kuznetsov 
1480aefd80e8SVitaly Kuznetsov 	rndis_device = get_rndis_device();
1481aefd80e8SVitaly Kuznetsov 	if (!rndis_device)
1482aefd80e8SVitaly Kuznetsov 		return ERR_PTR(-ENODEV);
1483aefd80e8SVitaly Kuznetsov 
1484aefd80e8SVitaly Kuznetsov 	/* Let the inner driver handle this first to create the netvsc channel
1485aefd80e8SVitaly Kuznetsov 	 * NOTE! Once the channel is created, we may get a receive callback
1486aefd80e8SVitaly Kuznetsov 	 * (RndisFilterOnReceive()) before this call is completed
1487aefd80e8SVitaly Kuznetsov 	 */
1488aefd80e8SVitaly Kuznetsov 	net_device = netvsc_device_add(dev, device_info);
1489aefd80e8SVitaly Kuznetsov 	if (IS_ERR(net_device)) {
1490aefd80e8SVitaly Kuznetsov 		kfree(rndis_device);
1491aefd80e8SVitaly Kuznetsov 		return net_device;
1492aefd80e8SVitaly Kuznetsov 	}
1493aefd80e8SVitaly Kuznetsov 
1494aefd80e8SVitaly Kuznetsov 	/* Initialize the rndis device */
1495aefd80e8SVitaly Kuznetsov 	net_device->max_chn = 1;
1496aefd80e8SVitaly Kuznetsov 	net_device->num_chn = 1;
1497aefd80e8SVitaly Kuznetsov 
1498aefd80e8SVitaly Kuznetsov 	net_device->extension = rndis_device;
1499aefd80e8SVitaly Kuznetsov 	rndis_device->ndev = net;
1500aefd80e8SVitaly Kuznetsov 
1501aefd80e8SVitaly Kuznetsov 	/* Send the rndis initialization message */
1502aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_init_device(rndis_device, net_device);
1503aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1504aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1505aefd80e8SVitaly Kuznetsov 
1506aefd80e8SVitaly Kuznetsov 	/* Get the MTU from the host */
1507aefd80e8SVitaly Kuznetsov 	size = sizeof(u32);
1508aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device(rndis_device, net_device,
1509aefd80e8SVitaly Kuznetsov 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
1510aefd80e8SVitaly Kuznetsov 					&mtu, &size);
1511aefd80e8SVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
1512aefd80e8SVitaly Kuznetsov 		net->mtu = mtu;
1513aefd80e8SVitaly Kuznetsov 
1514aefd80e8SVitaly Kuznetsov 	/* Get the mac address */
1515aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device_mac(rndis_device, net_device);
1516aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1517aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1518aefd80e8SVitaly Kuznetsov 
1519aefd80e8SVitaly Kuznetsov 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
1520aefd80e8SVitaly Kuznetsov 
15210fe554a4SStephen Hemminger 	/* Get friendly name as ifalias*/
15220fe554a4SStephen Hemminger 	if (!net->ifalias)
15230fe554a4SStephen Hemminger 		rndis_get_friendly_name(net, rndis_device, net_device);
15240fe554a4SStephen Hemminger 
1525aefd80e8SVitaly Kuznetsov 	/* Query and set hardware capabilities */
1526aefd80e8SVitaly Kuznetsov 	ret = rndis_netdev_set_hwcaps(rndis_device, net_device);
1527aefd80e8SVitaly Kuznetsov 	if (ret != 0)
15284a0e70aeSKY Srinivasan 		goto err_dev_remv;
15294a0e70aeSKY Srinivasan 
1530867047c4Sstephen hemminger 	rndis_filter_query_device_link_status(rndis_device, net_device);
153195fa0405SHaiyang Zhang 
153293ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
153395fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
1534dedb459eSHaiyang Zhang 		   rndis_device->link_state ? "down" : "up");
153595fa0405SHaiyang Zhang 
15365b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
153755be9f25SMohammed Gamal 		goto out;
15385b54dac8SHaiyang Zhang 
1539867047c4Sstephen hemminger 	rndis_filter_query_link_speed(rndis_device, net_device);
1540b37879e6SHaiyang Zhang 
15415b54dac8SHaiyang Zhang 	/* vRSS setup */
15425b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
1543867047c4Sstephen hemminger 	ret = rndis_filter_query_device(rndis_device, net_device,
15445b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
15455b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
15465b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
15475b54dac8SHaiyang Zhang 		goto out;
15485b54dac8SHaiyang Zhang 
154925a39f7fSHaiyang Zhang 	/* This guarantees that num_possible_rss_qs <= num_online_cpus */
155025a39f7fSHaiyang Zhang 	num_possible_rss_qs = min_t(u32, num_online_cpus(),
15513071ada4Sstephen hemminger 				    rsscap.num_recv_que);
15523071ada4Sstephen hemminger 
15533071ada4Sstephen hemminger 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs);
15548ebdcc52SAndrew Schwartzmeyer 
15558ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
15563071ada4Sstephen hemminger 	net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
1557ff4a4419Sstephen hemminger 
1558b0689faaSHaiyang Zhang 	if (!netif_is_rxfh_configured(net)) {
1559ff4a4419Sstephen hemminger 		for (i = 0; i < ITAB_NUM; i++)
1560b0689faaSHaiyang Zhang 			ndc->rx_table[i] = ethtool_rxfh_indir_default(
156147371300SHaiyang Zhang 						i, net_device->num_chn);
1562b0689faaSHaiyang Zhang 	}
1563ff4a4419Sstephen hemminger 
1564732e4985Sstephen hemminger 	atomic_set(&net_device->open_chn, 1);
15658195b139SStephen Hemminger 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
15665b54dac8SHaiyang Zhang 
15677426b1a5Sstephen hemminger 	for (i = 1; i < net_device->num_chn; i++) {
15687426b1a5Sstephen hemminger 		ret = netvsc_alloc_recv_comp_ring(net_device, i);
15697426b1a5Sstephen hemminger 		if (ret) {
15707426b1a5Sstephen hemminger 			while (--i != 0)
15717426b1a5Sstephen hemminger 				vfree(net_device->chan_table[i].mrc.slots);
15727426b1a5Sstephen hemminger 			goto out;
15737426b1a5Sstephen hemminger 		}
15747426b1a5Sstephen hemminger 	}
15757426b1a5Sstephen hemminger 
15768195b139SStephen Hemminger 	for (i = 1; i < net_device->num_chn; i++)
15778195b139SStephen Hemminger 		netif_napi_add(net, &net_device->chan_table[i].napi,
15788195b139SStephen Hemminger 			       netvsc_poll, NAPI_POLL_WEIGHT);
15795b54dac8SHaiyang Zhang 
15803ffe64f1SStephen Hemminger 	return net_device;
15815362855aSVitaly Kuznetsov 
15825b54dac8SHaiyang Zhang out:
15833ffe64f1SStephen Hemminger 	/* setting up multiple channels failed */
158459995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
15855b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
1586b19b4634STakashi Iwai 	return net_device;
15874a0e70aeSKY Srinivasan 
15884a0e70aeSKY Srinivasan err_dev_remv:
15892289f0aaSstephen hemminger 	rndis_filter_device_remove(dev, net_device);
15909749fed5Sstephen hemminger 	return ERR_PTR(ret);
159195fa0405SHaiyang Zhang }
159295fa0405SHaiyang Zhang 
15932289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev,
15942289f0aaSstephen hemminger 				struct netvsc_device *net_dev)
159595fa0405SHaiyang Zhang {
159695fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1597d66ab514SHaiyang Zhang 
159895fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
15990e96460eSStephen Hemminger 	rndis_filter_halt_device(net_dev, rndis_dev);
160095fa0405SHaiyang Zhang 
160195fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
160295fa0405SHaiyang Zhang }
160395fa0405SHaiyang Zhang 
16042f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
160595fa0405SHaiyang Zhang {
16062f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
160795fa0405SHaiyang Zhang 		return -EINVAL;
160895fa0405SHaiyang Zhang 
16092f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
161095fa0405SHaiyang Zhang }
161195fa0405SHaiyang Zhang 
16122f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
161395fa0405SHaiyang Zhang {
16145fccab3bSHaiyang Zhang 	if (!nvdev)
161595fa0405SHaiyang Zhang 		return -EINVAL;
161695fa0405SHaiyang Zhang 
16175fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
161895fa0405SHaiyang Zhang }
1619