Blame view
drivers/net/hyperv/netvsc_drv.c
39 KB
fceaf24a9 Staging: hv: add ... |
1 |
/* |
fceaf24a9 Staging: hv: add ... |
2 3 4 5 6 7 8 9 10 11 12 13 |
* Copyright (c) 2009, Microsoft Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with |
adf8d3ff6 drivers/net/*: Fi... |
14 |
* this program; if not, see <http://www.gnu.org/licenses/>. |
fceaf24a9 Staging: hv: add ... |
15 16 |
* * Authors: |
d0e94d17e Staging: hv: Fix ... |
17 |
* Haiyang Zhang <haiyangz@microsoft.com> |
fceaf24a9 Staging: hv: add ... |
18 |
* Hank Janssen <hjanssen@microsoft.com> |
fceaf24a9 Staging: hv: add ... |
19 |
*/ |
eb335bc42 staging: hv: repl... |
20 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
fceaf24a9 Staging: hv: add ... |
21 |
#include <linux/init.h> |
9079ce691 Staging: hv: netv... |
22 |
#include <linux/atomic.h> |
fceaf24a9 Staging: hv: add ... |
23 24 25 |
#include <linux/module.h> #include <linux/highmem.h> #include <linux/device.h> |
fceaf24a9 Staging: hv: add ... |
26 |
#include <linux/io.h> |
fceaf24a9 Staging: hv: add ... |
27 28 29 30 31 |
#include <linux/delay.h> #include <linux/netdevice.h> #include <linux/inetdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> |
c802db116 hyperv: Fix vlan_... |
32 |
#include <linux/if_vlan.h> |
fceaf24a9 Staging: hv: add ... |
33 |
#include <linux/in.h> |
5a0e3ad6a include cleanup: ... |
34 |
#include <linux/slab.h> |
fceaf24a9 Staging: hv: add ... |
35 36 37 38 |
#include <net/arp.h> #include <net/route.h> #include <net/sock.h> #include <net/pkt_sched.h> |
3f335ea21 Staging: hv: Incl... |
39 |
|
5ca7252a7 Staging: hv: netv... |
40 |
#include "hyperv_net.h" |
fceaf24a9 Staging: hv: add ... |
41 |
|
fa85a6c29 hyperv: Add a che... |
42 |
#define RING_SIZE_MIN 64 |
27a70af3f hv_netvsc: rework... |
43 |
#define LINKCHANGE_INT (2 * HZ) |
a060679c6 hv_netvsc: cleanu... |
44 45 46 47 48 |
#define NETVSC_HW_FEATURES (NETIF_F_RXCSUM | \ NETIF_F_SG | \ NETIF_F_TSO | \ NETIF_F_TSO6 | \ NETIF_F_HW_CSUM) |
a50af86dd netvsc: reduce ma... |
49 50 51 |
/* Restrict GSO size to account for NVGRE */ #define NETVSC_GSO_MAX_SIZE 62768 |
99c8da0f4 staging: hv: Doub... |
52 |
static int ring_size = 128; |
450d7a4b7 Staging: hv: ring... |
53 54 |
module_param(ring_size, int, S_IRUGO); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); |
fceaf24a9 Staging: hv: add ... |
55 |
|
e01ec2199 hv_netvsc: Proper... |
56 |
static int max_num_vrss_chns = 8; |
3f300ff41 hv_netvsc: introd... |
57 58 59 60 61 62 63 64 |
static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR; static int debug = -1; module_param(debug, int, S_IRUGO); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); |
d426b2e3d net/hyperv: Add s... |
65 66 |
static void do_set_multicast(struct work_struct *w) { |
792df8722 net/hyperv: Addin... |
67 68 |
struct net_device_context *ndevctx = container_of(w, struct net_device_context, work); |
0a1275ca5 hv_netvsc: get ri... |
69 70 71 |
struct hv_device *device_obj = ndevctx->device_ctx; struct net_device *ndev = hv_get_drvdata(device_obj); struct netvsc_device *nvdev = ndevctx->nvdev; |
d426b2e3d net/hyperv: Add s... |
72 |
struct rndis_device *rdev; |
0a1275ca5 hv_netvsc: get ri... |
73 |
if (!nvdev) |
792df8722 net/hyperv: Addin... |
74 |
return; |
d426b2e3d net/hyperv: Add s... |
75 76 77 |
rdev = nvdev->extension; if (rdev == NULL) |
792df8722 net/hyperv: Addin... |
78 |
return; |
d426b2e3d net/hyperv: Add s... |
79 |
|
0a1275ca5 hv_netvsc: get ri... |
80 |
if (ndev->flags & IFF_PROMISC) |
d426b2e3d net/hyperv: Add s... |
81 82 83 84 85 86 87 |
rndis_filter_set_packet_filter(rdev, NDIS_PACKET_TYPE_PROMISCUOUS); else rndis_filter_set_packet_filter(rdev, NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_DIRECTED); |
d426b2e3d net/hyperv: Add s... |
88 |
} |
4e9bfefa3 Staging: hv: remo... |
89 |
static void netvsc_set_multicast_list(struct net_device *net) |
fceaf24a9 Staging: hv: add ... |
90 |
{ |
792df8722 net/hyperv: Addin... |
91 |
struct net_device_context *net_device_ctx = netdev_priv(net); |
d426b2e3d net/hyperv: Add s... |
92 |
|
792df8722 net/hyperv: Addin... |
93 |
schedule_work(&net_device_ctx->work); |
fceaf24a9 Staging: hv: add ... |
94 |
} |
fceaf24a9 Staging: hv: add ... |
95 96 |
static int netvsc_open(struct net_device *net) { |
2f5fa6c86 hv_netvsc: pass s... |
97 |
struct netvsc_device *nvdev = net_device_to_netvsc_device(net); |
891de74d6 hyperv: Fix the c... |
98 |
struct rndis_device *rdev; |
02fafbc61 Staging: hv: codi... |
99 |
int ret = 0; |
fceaf24a9 Staging: hv: add ... |
100 |
|
891de74d6 hyperv: Fix the c... |
101 |
netif_carrier_off(net); |
d515d0ff3 staging: hv: remo... |
102 |
/* Open up the device */ |
2f5fa6c86 hv_netvsc: pass s... |
103 |
ret = rndis_filter_open(nvdev); |
d515d0ff3 staging: hv: remo... |
104 105 106 107 |
if (ret != 0) { netdev_err(net, "unable to open device (ret %d). ", ret); return ret; |
fceaf24a9 Staging: hv: add ... |
108 |
} |
2de8530ba hv_netvsc: Add cl... |
109 |
netif_tx_wake_all_queues(net); |
d515d0ff3 staging: hv: remo... |
110 |
|
891de74d6 hyperv: Fix the c... |
111 112 113 |
rdev = nvdev->extension; if (!rdev->link_state) netif_carrier_on(net); |
fceaf24a9 Staging: hv: add ... |
114 115 |
return ret; } |
fceaf24a9 Staging: hv: add ... |
116 117 |
static int netvsc_close(struct net_device *net) { |
fceaf24a9 Staging: hv: add ... |
118 |
struct net_device_context *net_device_ctx = netdev_priv(net); |
3d541ac5a hv_netvsc: untang... |
119 |
struct netvsc_device *nvdev = net_device_ctx->nvdev; |
02fafbc61 Staging: hv: codi... |
120 |
int ret; |
2de8530ba hv_netvsc: Add cl... |
121 122 |
u32 aread, awrite, i, msec = 10, retry = 0, retry_max = 20; struct vmbus_channel *chn; |
fceaf24a9 Staging: hv: add ... |
123 |
|
0a282538c net/hyperv: Use n... |
124 |
netif_tx_disable(net); |
fceaf24a9 Staging: hv: add ... |
125 |
|
792df8722 net/hyperv: Addin... |
126 127 |
/* Make sure netvsc_set_multicast_list doesn't re-enable filter! */ cancel_work_sync(&net_device_ctx->work); |
2f5fa6c86 hv_netvsc: pass s... |
128 |
ret = rndis_filter_close(nvdev); |
2de8530ba hv_netvsc: Add cl... |
129 |
if (ret != 0) { |
eb335bc42 staging: hv: repl... |
130 131 |
netdev_err(net, "unable to close device (ret %d). ", ret); |
2de8530ba hv_netvsc: Add cl... |
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
return ret; } /* Ensure pending bytes in ring are read */ while (true) { aread = 0; for (i = 0; i < nvdev->num_chn; i++) { chn = nvdev->chn_table[i]; if (!chn) continue; hv_get_ringbuffer_availbytes(&chn->inbound, &aread, &awrite); if (aread) break; hv_get_ringbuffer_availbytes(&chn->outbound, &aread, &awrite); if (aread) break; } retry++; if (retry > retry_max || aread == 0) break; msleep(msec); if (msec < 1000) msec *= 2; } if (aread) { netdev_err(net, "Ring buffer not empty after closing rndis "); ret = -ETIMEDOUT; } |
fceaf24a9 Staging: hv: add ... |
171 |
|
fceaf24a9 Staging: hv: add ... |
172 173 |
return ret; } |
8a00251a3 Drivers: net: hyp... |
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, int pkt_type) { struct rndis_packet *rndis_pkt; struct rndis_per_packet_info *ppi; rndis_pkt = &msg->msg.pkt; rndis_pkt->data_offset += ppi_size; ppi = (struct rndis_per_packet_info *)((void *)rndis_pkt + rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_len); ppi->size = ppi_size; ppi->type = pkt_type; ppi->ppi_offset = sizeof(struct rndis_per_packet_info); rndis_pkt->per_pkt_info_len += ppi_size; return ppi; } |
5b54dac85 hyperv: Add suppo... |
194 195 196 197 |
static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback) { struct net_device_context *net_device_ctx = netdev_priv(ndev); |
3d541ac5a hv_netvsc: untang... |
198 |
struct netvsc_device *nvsc_dev = net_device_ctx->nvdev; |
5b54dac85 hyperv: Add suppo... |
199 200 201 202 203 |
u32 hash; u16 q_idx = 0; if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1) return 0; |
757647e10 hv_netvsc: use sk... |
204 205 206 |
hash = skb_get_hash(skb); q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] % ndev->real_num_tx_queues; |
5b54dac85 hyperv: Add suppo... |
207 |
|
8b9fbe1ac hv_netvsc: move s... |
208 209 |
if (!nvsc_dev->chn_table[q_idx]) q_idx = 0; |
5b54dac85 hyperv: Add suppo... |
210 211 |
return q_idx; } |
54a7357f7 Drivers: net: hyp... |
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, struct hv_page_buffer *pb) { int j = 0; /* Deal with compund pages by ignoring unused part * of the page. */ page += (offset >> PAGE_SHIFT); offset &= ~PAGE_MASK; while (len > 0) { unsigned long bytes; bytes = PAGE_SIZE - offset; if (bytes > len) bytes = len; pb[j].pfn = page_to_pfn(page); pb[j].offset = offset; pb[j].len = bytes; offset += bytes; len -= bytes; if (offset == PAGE_SIZE && len) { page++; offset = 0; j++; } } return j + 1; } |
8a00251a3 Drivers: net: hyp... |
245 |
static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, |
a9f2e2d65 hv_netvsc: Elimin... |
246 247 |
struct hv_netvsc_packet *packet, struct hv_page_buffer **page_buf) |
54a7357f7 Drivers: net: hyp... |
248 |
{ |
a9f2e2d65 hv_netvsc: Elimin... |
249 |
struct hv_page_buffer *pb = *page_buf; |
54a7357f7 Drivers: net: hyp... |
250 251 252 253 254 255 |
u32 slots_used = 0; char *data = skb->data; int frags = skb_shinfo(skb)->nr_frags; int i; /* The packet is laid out thus: |
aa0a34be6 hv_netvsc: Implem... |
256 |
* 1. hdr: RNDIS header and PPI |
54a7357f7 Drivers: net: hyp... |
257 258 259 260 261 262 263 |
* 2. skb linear data * 3. skb fragment data */ if (hdr != NULL) slots_used += fill_pg_buf(virt_to_page(hdr), offset_in_page(hdr), len, &pb[slots_used]); |
aa0a34be6 hv_netvsc: Implem... |
264 265 |
packet->rmsg_size = len; packet->rmsg_pgcnt = slots_used; |
54a7357f7 Drivers: net: hyp... |
266 267 268 269 270 271 272 273 274 275 276 |
slots_used += fill_pg_buf(virt_to_page(data), offset_in_page(data), skb_headlen(skb), &pb[slots_used]); for (i = 0; i < frags; i++) { skb_frag_t *frag = skb_shinfo(skb)->frags + i; slots_used += fill_pg_buf(skb_frag_page(frag), frag->page_offset, skb_frag_size(frag), &pb[slots_used]); } |
8a00251a3 Drivers: net: hyp... |
277 |
return slots_used; |
54a7357f7 Drivers: net: hyp... |
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
} static int count_skb_frag_slots(struct sk_buff *skb) { int i, frags = skb_shinfo(skb)->nr_frags; int pages = 0; for (i = 0; i < frags; i++) { skb_frag_t *frag = skb_shinfo(skb)->frags + i; unsigned long size = skb_frag_size(frag); unsigned long offset = frag->page_offset; /* Skip unused frames from start of page */ offset &= ~PAGE_MASK; pages += PFN_UP(offset + size); } return pages; } static int netvsc_get_slots(struct sk_buff *skb) { char *data = skb->data; unsigned int offset = offset_in_page(data); unsigned int len = skb_headlen(skb); int slots; int frag_slots; slots = DIV_ROUND_UP(offset + len, PAGE_SIZE); frag_slots = count_skb_frag_slots(skb); return slots + frag_slots; } |
08cd04bf6 Drivers: net: hyp... |
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
static u32 get_net_transport_info(struct sk_buff *skb, u32 *trans_off) { u32 ret_val = TRANSPORT_INFO_NOT_IP; if ((eth_hdr(skb)->h_proto != htons(ETH_P_IP)) && (eth_hdr(skb)->h_proto != htons(ETH_P_IPV6))) { goto not_ip; } *trans_off = skb_transport_offset(skb); if ((eth_hdr(skb)->h_proto == htons(ETH_P_IP))) { struct iphdr *iphdr = ip_hdr(skb); if (iphdr->protocol == IPPROTO_TCP) ret_val = TRANSPORT_INFO_IPV4_TCP; else if (iphdr->protocol == IPPROTO_UDP) ret_val = TRANSPORT_INFO_IPV4_UDP; } else { if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP) ret_val = TRANSPORT_INFO_IPV6_TCP; else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP) ret_val = TRANSPORT_INFO_IPV6_UDP; } not_ip: return ret_val; } |
02fafbc61 Staging: hv: codi... |
337 |
static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) |
fceaf24a9 Staging: hv: add ... |
338 |
{ |
fceaf24a9 Staging: hv: add ... |
339 |
struct net_device_context *net_device_ctx = netdev_priv(net); |
981a1bd85 hv_netvsc: use si... |
340 |
struct hv_netvsc_packet *packet = NULL; |
02fafbc61 Staging: hv: codi... |
341 |
int ret; |
8a00251a3 Drivers: net: hyp... |
342 343 344 345 |
unsigned int num_data_pgs; struct rndis_message *rndis_msg; struct rndis_packet *rndis_pkt; u32 rndis_msg_size; |
8a00251a3 Drivers: net: hyp... |
346 |
struct rndis_per_packet_info *ppi; |
08cd04bf6 Drivers: net: hyp... |
347 348 349 |
struct ndis_tcp_ip_checksum_info *csum_info; int hdr_offset; u32 net_trans_info; |
307f09952 hyperv: Add hash ... |
350 |
u32 hash; |
e88f7e078 hv_netvsc: try li... |
351 |
u32 skb_length; |
b08cc7915 hv_netvsc: Elimin... |
352 |
struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT]; |
a9f2e2d65 hv_netvsc: Elimin... |
353 |
struct hv_page_buffer *pb = page_buf; |
fceaf24a9 Staging: hv: add ... |
354 |
|
54a7357f7 Drivers: net: hyp... |
355 356 |
/* We will atmost need two pages to describe the rndis * header. We can only transmit MAX_PAGE_BUFFER_COUNT number |
e88f7e078 hv_netvsc: try li... |
357 358 |
* of pages in a single packet. If skb is scattered around * more pages we try linearizing it. |
54a7357f7 Drivers: net: hyp... |
359 |
*/ |
e88f7e078 hv_netvsc: try li... |
360 |
|
e88f7e078 hv_netvsc: try li... |
361 |
skb_length = skb->len; |
8a00251a3 Drivers: net: hyp... |
362 |
num_data_pgs = netvsc_get_slots(skb) + 2; |
0ab05141f hv_netvsc: rearra... |
363 364 |
if (unlikely(num_data_pgs > MAX_PAGE_BUFFER_COUNT)) { |
4323b47cf hv_netvsc: add et... |
365 366 367 368 |
++net_device_ctx->eth_stats.tx_scattered; if (skb_linearize(skb)) goto no_memory; |
0ab05141f hv_netvsc: rearra... |
369 370 371 |
num_data_pgs = netvsc_get_slots(skb) + 2; if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) { |
4323b47cf hv_netvsc: add et... |
372 |
++net_device_ctx->eth_stats.tx_too_big; |
0ab05141f hv_netvsc: rearra... |
373 374 |
goto drop; } |
54a7357f7 Drivers: net: hyp... |
375 |
} |
fceaf24a9 Staging: hv: add ... |
376 |
|
c0eb45403 hv_netvsc: Don't ... |
377 378 379 380 381 382 |
/* * Place the rndis header in the skb head room and * the skb->cb will be used for hv_netvsc_packet * structure. */ ret = skb_cow_head(skb, RNDIS_AND_PPI_SIZE); |
4323b47cf hv_netvsc: add et... |
383 384 |
if (ret) goto no_memory; |
c0eb45403 hv_netvsc: Don't ... |
385 386 387 388 |
/* Use the skb control buffer for building up the packet */ BUILD_BUG_ON(sizeof(struct hv_netvsc_packet) > FIELD_SIZEOF(struct sk_buff, cb)); packet = (struct hv_netvsc_packet *)skb->cb; |
fceaf24a9 Staging: hv: add ... |
389 |
|
5b54dac85 hyperv: Add suppo... |
390 |
packet->q_idx = skb_get_queue_mapping(skb); |
4d447c9a6 net/hyperv: Add s... |
391 |
packet->total_data_buflen = skb->len; |
fceaf24a9 Staging: hv: add ... |
392 |
|
c0eb45403 hv_netvsc: Don't ... |
393 |
rndis_msg = (struct rndis_message *)skb->head; |
b08cc7915 hv_netvsc: Elimin... |
394 |
|
24476760e hv_netvsc: Elimin... |
395 |
memset(rndis_msg, 0, RNDIS_AND_PPI_SIZE); |
fceaf24a9 Staging: hv: add ... |
396 |
|
8a00251a3 Drivers: net: hyp... |
397 |
/* Add the rndis header */ |
8a00251a3 Drivers: net: hyp... |
398 399 400 401 402 403 404 405 |
rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET; rndis_msg->msg_len = packet->total_data_buflen; rndis_pkt = &rndis_msg->msg.pkt; rndis_pkt->data_offset = sizeof(struct rndis_packet); rndis_pkt->data_len = packet->total_data_buflen; rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet); rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); |
307f09952 hyperv: Add hash ... |
406 407 408 409 410 411 412 |
hash = skb_get_hash_raw(skb); if (hash != 0 && net->real_num_tx_queues > 1) { rndis_msg_size += NDIS_HASH_PPI_SIZE; ppi = init_ppi_data(rndis_msg, NDIS_HASH_PPI_SIZE, NBL_HASH_VALUE); *(u32 *)((void *)ppi + ppi->ppi_offset) = hash; } |
0ab05141f hv_netvsc: rearra... |
413 |
if (skb_vlan_tag_present(skb)) { |
8a00251a3 Drivers: net: hyp... |
414 415 416 417 418 419 420 |
struct ndis_pkt_8021q_info *vlan; rndis_msg_size += NDIS_VLAN_PPI_SIZE; ppi = init_ppi_data(rndis_msg, NDIS_VLAN_PPI_SIZE, IEEE_8021Q_INFO); vlan = (struct ndis_pkt_8021q_info *)((void *)ppi + ppi->ppi_offset); |
760d1e36c hv_netvsc: Elimin... |
421 422 |
vlan->vlanid = skb->vlan_tci & VLAN_VID_MASK; vlan->pri = (skb->vlan_tci & VLAN_PRIO_MASK) >> |
8a00251a3 Drivers: net: hyp... |
423 424 |
VLAN_PRIO_SHIFT; } |
08cd04bf6 Drivers: net: hyp... |
425 |
net_trans_info = get_net_transport_info(skb, &hdr_offset); |
08cd04bf6 Drivers: net: hyp... |
426 427 428 429 430 |
/* * Setup the sendside checksum offload only if this is not a * GSO packet. */ |
52ccd6318 net/hyperv: avoid... |
431 |
if ((net_trans_info & (INFO_TCP | INFO_UDP)) && skb_is_gso(skb)) { |
0ab05141f hv_netvsc: rearra... |
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
struct ndis_tcp_lso_info *lso_info; rndis_msg_size += NDIS_LSO_PPI_SIZE; ppi = init_ppi_data(rndis_msg, NDIS_LSO_PPI_SIZE, TCP_LARGESEND_PKTINFO); lso_info = (struct ndis_tcp_lso_info *)((void *)ppi + ppi->ppi_offset); lso_info->lso_v2_transmit.type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE; if (net_trans_info & (INFO_IPV4 << 16)) { lso_info->lso_v2_transmit.ip_version = NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4; ip_hdr(skb)->tot_len = 0; ip_hdr(skb)->check = 0; tcp_hdr(skb)->check = ~csum_tcpudp_magic(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); } else { lso_info->lso_v2_transmit.ip_version = NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6; ipv6_hdr(skb)->payload_len = 0; tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); } lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset; lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size; |
ad19bc8a9 netvsc: fix check... |
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
} else if (skb->ip_summed == CHECKSUM_PARTIAL) { if (net_trans_info & INFO_TCP) { rndis_msg_size += NDIS_CSUM_PPI_SIZE; ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE, TCPIP_CHKSUM_PKTINFO); csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi + ppi->ppi_offset); if (net_trans_info & (INFO_IPV4 << 16)) csum_info->transmit.is_ipv4 = 1; else csum_info->transmit.is_ipv6 = 1; csum_info->transmit.tcp_checksum = 1; csum_info->transmit.tcp_header_offset = hdr_offset; } else { /* UDP checksum (and other) offload is not supported. */ if (skb_checksum_help(skb)) goto drop; } |
08cd04bf6 Drivers: net: hyp... |
481 |
} |
8a00251a3 Drivers: net: hyp... |
482 483 |
/* Start filling in the page buffers with the rndis hdr */ rndis_msg->msg_len += rndis_msg_size; |
942396b01 hyperv: Fix the t... |
484 |
packet->total_data_buflen = rndis_msg->msg_len; |
8a00251a3 Drivers: net: hyp... |
485 |
packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, |
a9f2e2d65 hv_netvsc: Elimin... |
486 |
skb, packet, &pb); |
8a00251a3 Drivers: net: hyp... |
487 |
|
76d13b568 hv_netvsc: add so... |
488 489 |
/* timestamp packet in software */ skb_tx_timestamp(skb); |
3a3d9a0a7 hv_netvsc: Elimin... |
490 491 |
ret = netvsc_send(net_device_ctx->device_ctx, packet, rndis_msg, &pb, skb); |
0ab05141f hv_netvsc: rearra... |
492 |
if (likely(ret == 0)) { |
4323b47cf hv_netvsc: add et... |
493 |
struct netvsc_stats *tx_stats = this_cpu_ptr(net_device_ctx->tx_stats); |
4b02b58b5 hv_netvsc: change... |
494 |
u64_stats_update_begin(&tx_stats->syncp); |
7eafd9b40 hv_netvsc: use pe... |
495 496 |
tx_stats->packets++; tx_stats->bytes += skb_length; |
4b02b58b5 hv_netvsc: change... |
497 |
u64_stats_update_end(&tx_stats->syncp); |
0ab05141f hv_netvsc: rearra... |
498 |
return NETDEV_TX_OK; |
fceaf24a9 Staging: hv: add ... |
499 |
} |
4323b47cf hv_netvsc: add et... |
500 501 502 |
if (ret == -EAGAIN) { ++net_device_ctx->eth_stats.tx_busy; |
0ab05141f hv_netvsc: rearra... |
503 |
return NETDEV_TX_BUSY; |
4323b47cf hv_netvsc: add et... |
504 505 506 507 |
} if (ret == -ENOSPC) ++net_device_ctx->eth_stats.tx_no_space; |
0ab05141f hv_netvsc: rearra... |
508 509 510 511 |
drop: dev_kfree_skb_any(skb); net->stats.tx_dropped++; |
fceaf24a9 Staging: hv: add ... |
512 |
|
0ab05141f hv_netvsc: rearra... |
513 |
return NETDEV_TX_OK; |
4323b47cf hv_netvsc: add et... |
514 515 516 517 |
no_memory: ++net_device_ctx->eth_stats.tx_no_memory; goto drop; |
fceaf24a9 Staging: hv: add ... |
518 |
} |
3e1895195 staging: hv: Corr... |
519 |
/* |
02fafbc61 Staging: hv: codi... |
520 521 |
* netvsc_linkstatus_callback - Link up/down notification */ |
90ef117a3 Staging: hv: netv... |
522 |
void netvsc_linkstatus_callback(struct hv_device *device_obj, |
3a494e710 hyperv: Add handl... |
523 |
struct rndis_message *resp) |
fceaf24a9 Staging: hv: add ... |
524 |
{ |
3a494e710 hyperv: Add handl... |
525 |
struct rndis_indicate_status *indicate = &resp->msg.indicate_status; |
2ddd5e5fb Staging: hv: netv... |
526 |
struct net_device *net; |
c996edcf1 staging: hv: Fix ... |
527 |
struct net_device_context *ndev_ctx; |
27a70af3f hv_netvsc: rework... |
528 529 |
struct netvsc_reconfig *event; unsigned long flags; |
891de74d6 hyperv: Fix the c... |
530 |
|
7f5d5af0b hv_netvsc: Add ha... |
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
net = hv_get_drvdata(device_obj); if (!net) return; ndev_ctx = netdev_priv(net); /* Update the physical link speed when changing to another vSwitch */ if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) { u32 speed; speed = *(u32 *)((void *)indicate + indicate-> status_buf_offset) / 10000; ndev_ctx->speed = speed; return; } /* Handle these link change statuses below */ |
27a70af3f hv_netvsc: rework... |
549 550 551 |
if (indicate->status != RNDIS_STATUS_NETWORK_CHANGE && indicate->status != RNDIS_STATUS_MEDIA_CONNECT && indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT) |
3a494e710 hyperv: Add handl... |
552 |
return; |
891de74d6 hyperv: Fix the c... |
553 |
|
7f5d5af0b hv_netvsc: Add ha... |
554 |
if (net->reg_state != NETREG_REGISTERED) |
fceaf24a9 Staging: hv: add ... |
555 |
return; |
fceaf24a9 Staging: hv: add ... |
556 |
|
27a70af3f hv_netvsc: rework... |
557 558 559 560 561 562 563 564 565 566 |
event = kzalloc(sizeof(*event), GFP_ATOMIC); if (!event) return; event->event = indicate->status; spin_lock_irqsave(&ndev_ctx->lock, flags); list_add_tail(&event->list, &ndev_ctx->reconfig_events); spin_unlock_irqrestore(&ndev_ctx->lock, flags); schedule_delayed_work(&ndev_ctx->dwork, 0); |
fceaf24a9 Staging: hv: add ... |
567 |
} |
84bf9cefb hv_netvsc: Implem... |
568 |
static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net, |
e3d605ed4 Drivers: net: hyp... |
569 |
struct hv_netvsc_packet *packet, |
25b85ee89 hv_netvsc: Elimin... |
570 |
struct ndis_tcp_ip_checksum_info *csum_info, |
84bf9cefb hv_netvsc: Implem... |
571 |
void *data, u16 vlan_tci) |
fceaf24a9 Staging: hv: add ... |
572 |
{ |
fceaf24a9 Staging: hv: add ... |
573 |
struct sk_buff *skb; |
fceaf24a9 Staging: hv: add ... |
574 |
|
72a2f5bd5 staging: hv: Conv... |
575 |
skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen); |
84bf9cefb hv_netvsc: Implem... |
576 577 |
if (!skb) return skb; |
fceaf24a9 Staging: hv: add ... |
578 |
|
02fafbc61 Staging: hv: codi... |
579 580 581 582 |
/* * Copy to skb. This copy is needed here since the memory pointed by * hv_netvsc_packet cannot be deallocated */ |
84bf9cefb hv_netvsc: Implem... |
583 584 |
memcpy(skb_put(skb, packet->total_data_buflen), data, packet->total_data_buflen); |
fceaf24a9 Staging: hv: add ... |
585 586 |
skb->protocol = eth_type_trans(skb, net); |
e52fed717 netvsc: fix incor... |
587 588 589 590 591 592 593 594 595 596 597 |
/* skb is already created with CHECKSUM_NONE */ skb_checksum_none_assert(skb); /* * In Linux, the IP checksum is always checked. * Do L4 checksum offload if enabled and present. */ if (csum_info && (net->features & NETIF_F_RXCSUM)) { if (csum_info->receive.tcp_checksum_succeeded || csum_info->receive.udp_checksum_succeeded) |
e3d605ed4 Drivers: net: hyp... |
598 |
skb->ip_summed = CHECKSUM_UNNECESSARY; |
e3d605ed4 Drivers: net: hyp... |
599 |
} |
760d1e36c hv_netvsc: Elimin... |
600 |
if (vlan_tci & VLAN_TAG_PRESENT) |
93725cbd2 Fix the VLAN_TAG_... |
601 |
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), |
760d1e36c hv_netvsc: Elimin... |
602 |
vlan_tci); |
fceaf24a9 Staging: hv: add ... |
603 |
|
84bf9cefb hv_netvsc: Implem... |
604 605 606 607 608 609 610 611 612 613 614 615 616 617 |
return skb; } /* * netvsc_recv_callback - Callback when we receive a packet from the * "wire" on the specified device. */ int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, void **data, struct ndis_tcp_ip_checksum_info *csum_info, struct vmbus_channel *channel, u16 vlan_tci) { |
3d541ac5a hv_netvsc: untang... |
618 619 |
struct net_device *net = hv_get_drvdata(device_obj); struct net_device_context *net_device_ctx = netdev_priv(net); |
f207c10d9 hv_netvsc: use RC... |
620 |
struct net_device *vf_netdev; |
84bf9cefb hv_netvsc: Implem... |
621 |
struct sk_buff *skb; |
84bf9cefb hv_netvsc: Implem... |
622 |
struct netvsc_stats *rx_stats; |
84bf9cefb hv_netvsc: Implem... |
623 |
|
9cbcc4280 hv_netvsc: remove... |
624 |
if (net->reg_state != NETREG_REGISTERED) |
84bf9cefb hv_netvsc: Implem... |
625 |
return NVSP_STAT_FAIL; |
9cbcc4280 hv_netvsc: remove... |
626 627 628 629 630 631 632 |
/* * If necessary, inject this packet into the VF interface. * On Hyper-V, multicast and brodcast packets are only delivered * to the synthetic interface (after subjecting these to * policy filters on the host). Deliver these via the VF * interface in the guest. */ |
5b3df4401 netvsc: add rcu_r... |
633 |
rcu_read_lock(); |
f207c10d9 hv_netvsc: use RC... |
634 |
vf_netdev = rcu_dereference(net_device_ctx->vf_netdev); |
9cbcc4280 hv_netvsc: remove... |
635 636 |
if (vf_netdev && (vf_netdev->flags & IFF_UP)) net = vf_netdev; |
84bf9cefb hv_netvsc: Implem... |
637 638 639 640 641 |
/* Allocate a skb - TODO direct I/O to pages? */ skb = netvsc_alloc_recv_skb(net, packet, csum_info, *data, vlan_tci); if (unlikely(!skb)) { ++net->stats.rx_dropped; |
5b3df4401 netvsc: add rcu_r... |
642 |
rcu_read_unlock(); |
84bf9cefb hv_netvsc: Implem... |
643 644 |
return NVSP_STAT_FAIL; } |
5b54dac85 hyperv: Add suppo... |
645 |
|
9cbcc4280 hv_netvsc: remove... |
646 647 648 649 650 651 652 653 654 655 |
if (net != vf_netdev) skb_record_rx_queue(skb, channel->offermsg.offer.sub_channel_index); /* * Even if injecting the packet, record the statistics * on the synthetic device because modifying the VF device * statistics will not work correctly. */ rx_stats = this_cpu_ptr(net_device_ctx->rx_stats); |
4b02b58b5 hv_netvsc: change... |
656 |
u64_stats_update_begin(&rx_stats->syncp); |
7eafd9b40 hv_netvsc: use pe... |
657 658 |
rx_stats->packets++; rx_stats->bytes += packet->total_data_buflen; |
f7ad75b75 hv_netvsc: count ... |
659 660 661 662 663 |
if (skb->pkt_type == PACKET_BROADCAST) ++rx_stats->broadcast; else if (skb->pkt_type == PACKET_MULTICAST) ++rx_stats->multicast; |
4b02b58b5 hv_netvsc: change... |
664 |
u64_stats_update_end(&rx_stats->syncp); |
9495c282b Staging: hv: hand... |
665 |
|
02fafbc61 Staging: hv: codi... |
666 667 |
/* * Pass the skb back up. Network stack will deallocate the skb when it |
9495c282b Staging: hv: hand... |
668 669 |
* is done. * TODO - use NAPI? |
02fafbc61 Staging: hv: codi... |
670 |
*/ |
9495c282b Staging: hv: hand... |
671 |
netif_rx(skb); |
5b3df4401 netvsc: add rcu_r... |
672 |
rcu_read_unlock(); |
fceaf24a9 Staging: hv: add ... |
673 |
|
fceaf24a9 Staging: hv: add ... |
674 675 |
return 0; } |
f82f4ad7b Staging: hv: add ... |
676 677 678 |
static void netvsc_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { |
7826d43f2 ethtool: fix drvi... |
679 |
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); |
7826d43f2 ethtool: fix drvi... |
680 |
strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); |
f82f4ad7b Staging: hv: add ... |
681 |
} |
59995370d hyperv: Implement... |
682 683 684 685 |
static void netvsc_get_channels(struct net_device *net, struct ethtool_channels *channel) { struct net_device_context *net_device_ctx = netdev_priv(net); |
3d541ac5a hv_netvsc: untang... |
686 |
struct netvsc_device *nvdev = net_device_ctx->nvdev; |
59995370d hyperv: Implement... |
687 688 689 690 691 692 |
if (nvdev) { channel->max_combined = nvdev->max_chn; channel->combined_count = nvdev->num_chn; } } |
b5960e6e2 hv_netvsc: Implem... |
693 694 695 696 697 |
static int netvsc_set_channels(struct net_device *net, struct ethtool_channels *channels) { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *dev = net_device_ctx->device_ctx; |
3d541ac5a hv_netvsc: untang... |
698 |
struct netvsc_device *nvdev = net_device_ctx->nvdev; |
b5960e6e2 hv_netvsc: Implem... |
699 |
struct netvsc_device_info device_info; |
954591b9f hv_netvsc: Fix de... |
700 701 |
u32 num_chn; u32 max_chn; |
b5960e6e2 hv_netvsc: Implem... |
702 703 |
int ret = 0; bool recovering = false; |
6da7225f5 hv_netvsc: synchr... |
704 |
if (net_device_ctx->start_remove || !nvdev || nvdev->destroy) |
b5960e6e2 hv_netvsc: Implem... |
705 |
return -ENODEV; |
954591b9f hv_netvsc: Fix de... |
706 707 |
num_chn = nvdev->num_chn; max_chn = min_t(u32, nvdev->max_chn, num_online_cpus()); |
b5960e6e2 hv_netvsc: Implem... |
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 |
if (nvdev->nvsp_version < NVSP_PROTOCOL_VERSION_5) { pr_info("vRSS unsupported before NVSP Version 5 "); return -EINVAL; } /* We do not support rx, tx, or other */ if (!channels || channels->rx_count || channels->tx_count || channels->other_count || (channels->combined_count < 1)) return -EINVAL; if (channels->combined_count > max_chn) { pr_info("combined channels too high, using %d ", max_chn); channels->combined_count = max_chn; } ret = netvsc_close(net); if (ret) goto out; do_set: |
f580aec4b hv_netvsc: move s... |
733 |
net_device_ctx->start_remove = true; |
b5960e6e2 hv_netvsc: Implem... |
734 735 736 |
rndis_filter_device_remove(dev); nvdev->num_chn = channels->combined_count; |
b5960e6e2 hv_netvsc: Implem... |
737 738 739 740 741 742 743 744 745 746 747 748 749 750 |
memset(&device_info, 0, sizeof(device_info)); device_info.num_chn = nvdev->num_chn; /* passed to RNDIS */ device_info.ring_size = ring_size; device_info.max_num_vrss_chns = max_num_vrss_chns; ret = rndis_filter_device_add(dev, &device_info); if (ret) { if (recovering) { netdev_err(net, "unable to add netvsc device (ret %d) ", ret); return ret; } goto recover; } |
3d541ac5a hv_netvsc: untang... |
751 |
nvdev = net_device_ctx->nvdev; |
b5960e6e2 hv_netvsc: Implem... |
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 |
ret = netif_set_real_num_tx_queues(net, nvdev->num_chn); if (ret) { if (recovering) { netdev_err(net, "could not set tx queue count (ret %d) ", ret); return ret; } goto recover; } ret = netif_set_real_num_rx_queues(net, nvdev->num_chn); if (ret) { if (recovering) { netdev_err(net, "could not set rx queue count (ret %d) ", ret); return ret; } goto recover; } out: netvsc_open(net); |
f580aec4b hv_netvsc: move s... |
775 |
net_device_ctx->start_remove = false; |
1bdcec8a5 hv_netvsc: use st... |
776 777 |
/* We may have missed link change notifications */ schedule_delayed_work(&net_device_ctx->dwork, 0); |
b5960e6e2 hv_netvsc: Implem... |
778 779 780 781 782 783 784 785 786 787 788 789 790 |
return ret; recover: /* If the above failed, we attempt to recover through the same * process but with the original number of channels. */ netdev_err(net, "could not set channels, recovering "); recovering = true; channels->combined_count = num_chn; goto do_set; } |
49eb93892 hv_netvsc: add et... |
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 |
static bool netvsc_validate_ethtool_ss_cmd(const struct ethtool_cmd *cmd) { struct ethtool_cmd diff1 = *cmd; struct ethtool_cmd diff2 = {}; ethtool_cmd_speed_set(&diff1, 0); diff1.duplex = 0; /* advertising and cmd are usually set */ diff1.advertising = 0; diff1.cmd = 0; /* We set port to PORT_OTHER */ diff2.port = PORT_OTHER; return !memcmp(&diff1, &diff2, sizeof(diff1)); } static void netvsc_init_settings(struct net_device *dev) { struct net_device_context *ndc = netdev_priv(dev); ndc->speed = SPEED_UNKNOWN; ndc->duplex = DUPLEX_UNKNOWN; } static int netvsc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct net_device_context *ndc = netdev_priv(dev); ethtool_cmd_speed_set(cmd, ndc->speed); cmd->duplex = ndc->duplex; cmd->port = PORT_OTHER; return 0; } static int netvsc_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct net_device_context *ndc = netdev_priv(dev); u32 speed; speed = ethtool_cmd_speed(cmd); if (!ethtool_validate_speed(speed) || !ethtool_validate_duplex(cmd->duplex) || !netvsc_validate_ethtool_ss_cmd(cmd)) return -EINVAL; ndc->speed = speed; ndc->duplex = cmd->duplex; return 0; } |
4d447c9a6 net/hyperv: Add s... |
842 843 844 |
static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); |
3d541ac5a hv_netvsc: untang... |
845 846 |
struct netvsc_device *nvdev = ndevctx->nvdev; struct hv_device *hdev = ndevctx->device_ctx; |
4d447c9a6 net/hyperv: Add s... |
847 848 |
struct netvsc_device_info device_info; int limit = ETH_DATA_LEN; |
d212b4633 hv_netvsc: Fix ac... |
849 |
u32 num_chn; |
2de8530ba hv_netvsc: Add cl... |
850 |
int ret = 0; |
4d447c9a6 net/hyperv: Add s... |
851 |
|
6da7225f5 hv_netvsc: synchr... |
852 |
if (ndevctx->start_remove || !nvdev || nvdev->destroy) |
4d447c9a6 net/hyperv: Add s... |
853 |
return -ENODEV; |
a1eabb017 hyperv: Add lates... |
854 |
if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) |
4d3c9d37f hyperv: Add proce... |
855 |
limit = NETVSC_MTU - ETH_HLEN; |
4d447c9a6 net/hyperv: Add s... |
856 |
|
f9cbce34c hv_netvsc: Add su... |
857 |
if (mtu < NETVSC_MTU_MIN || mtu > limit) |
4d447c9a6 net/hyperv: Add s... |
858 |
return -EINVAL; |
2de8530ba hv_netvsc: Add cl... |
859 860 861 |
ret = netvsc_close(ndev); if (ret) goto out; |
d212b4633 hv_netvsc: Fix ac... |
862 |
num_chn = nvdev->num_chn; |
f580aec4b hv_netvsc: move s... |
863 |
ndevctx->start_remove = true; |
4d447c9a6 net/hyperv: Add s... |
864 865 866 |
rndis_filter_device_remove(hdev); ndev->mtu = mtu; |
8ebdcc52b hv_netvsc: Set vR... |
867 |
memset(&device_info, 0, sizeof(device_info)); |
4d447c9a6 net/hyperv: Add s... |
868 |
device_info.ring_size = ring_size; |
d212b4633 hv_netvsc: Fix ac... |
869 |
device_info.num_chn = num_chn; |
e01ec2199 hv_netvsc: Proper... |
870 |
device_info.max_num_vrss_chns = max_num_vrss_chns; |
4d447c9a6 net/hyperv: Add s... |
871 |
rndis_filter_device_add(hdev, &device_info); |
4d447c9a6 net/hyperv: Add s... |
872 |
|
2de8530ba hv_netvsc: Add cl... |
873 874 |
out: netvsc_open(ndev); |
f580aec4b hv_netvsc: move s... |
875 |
ndevctx->start_remove = false; |
2de8530ba hv_netvsc: Add cl... |
876 |
|
1bdcec8a5 hv_netvsc: use st... |
877 878 |
/* We may have missed link change notifications */ schedule_delayed_work(&ndevctx->dwork, 0); |
2de8530ba hv_netvsc: Add cl... |
879 |
return ret; |
4d447c9a6 net/hyperv: Add s... |
880 |
} |
7eafd9b40 hv_netvsc: use pe... |
881 882 883 884 885 886 887 888 889 890 891 |
static struct rtnl_link_stats64 *netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { struct net_device_context *ndev_ctx = netdev_priv(net); int cpu; for_each_possible_cpu(cpu) { struct netvsc_stats *tx_stats = per_cpu_ptr(ndev_ctx->tx_stats, cpu); struct netvsc_stats *rx_stats = per_cpu_ptr(ndev_ctx->rx_stats, cpu); |
f7ad75b75 hv_netvsc: count ... |
892 |
u64 tx_packets, tx_bytes, rx_packets, rx_bytes, rx_multicast; |
7eafd9b40 hv_netvsc: use pe... |
893 894 895 |
unsigned int start; do { |
4b02b58b5 hv_netvsc: change... |
896 |
start = u64_stats_fetch_begin_irq(&tx_stats->syncp); |
7eafd9b40 hv_netvsc: use pe... |
897 898 |
tx_packets = tx_stats->packets; tx_bytes = tx_stats->bytes; |
4b02b58b5 hv_netvsc: change... |
899 |
} while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); |
7eafd9b40 hv_netvsc: use pe... |
900 901 |
do { |
4b02b58b5 hv_netvsc: change... |
902 |
start = u64_stats_fetch_begin_irq(&rx_stats->syncp); |
7eafd9b40 hv_netvsc: use pe... |
903 904 |
rx_packets = rx_stats->packets; rx_bytes = rx_stats->bytes; |
f7ad75b75 hv_netvsc: count ... |
905 |
rx_multicast = rx_stats->multicast + rx_stats->broadcast; |
4b02b58b5 hv_netvsc: change... |
906 |
} while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); |
7eafd9b40 hv_netvsc: use pe... |
907 908 909 910 911 |
t->tx_bytes += tx_bytes; t->tx_packets += tx_packets; t->rx_bytes += rx_bytes; t->rx_packets += rx_packets; |
f7ad75b75 hv_netvsc: count ... |
912 |
t->multicast += rx_multicast; |
7eafd9b40 hv_netvsc: use pe... |
913 914 915 916 917 918 919 920 921 922 |
} t->tx_dropped = net->stats.tx_dropped; t->tx_errors = net->stats.tx_dropped; t->rx_dropped = net->stats.rx_dropped; t->rx_errors = net->stats.rx_errors; return t; } |
1ce09e899 hyperv: Add suppo... |
923 924 925 |
static int netvsc_set_mac_addr(struct net_device *ndev, void *p) { |
1ce09e899 hyperv: Add suppo... |
926 |
struct sockaddr *addr = p; |
9a4c831eb net/hyperv: fix w... |
927 |
char save_adr[ETH_ALEN]; |
1ce09e899 hyperv: Add suppo... |
928 929 930 931 932 933 934 935 936 |
unsigned char save_aatype; int err; memcpy(save_adr, ndev->dev_addr, ETH_ALEN); save_aatype = ndev->addr_assign_type; err = eth_mac_addr(ndev, p); if (err != 0) return err; |
e834da9a4 hv_netvsc: pass s... |
937 |
err = rndis_filter_set_device_mac(ndev, addr->sa_data); |
1ce09e899 hyperv: Add suppo... |
938 939 940 941 942 943 944 945 |
if (err != 0) { /* roll back to saved MAC */ memcpy(ndev->dev_addr, save_adr, ETH_ALEN); ndev->addr_assign_type = save_aatype; } return err; } |
4323b47cf hv_netvsc: add et... |
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 |
static const struct { char name[ETH_GSTRING_LEN]; u16 offset; } netvsc_stats[] = { { "tx_scattered", offsetof(struct netvsc_ethtool_stats, tx_scattered) }, { "tx_no_memory", offsetof(struct netvsc_ethtool_stats, tx_no_memory) }, { "tx_no_space", offsetof(struct netvsc_ethtool_stats, tx_no_space) }, { "tx_too_big", offsetof(struct netvsc_ethtool_stats, tx_too_big) }, { "tx_busy", offsetof(struct netvsc_ethtool_stats, tx_busy) }, }; static int netvsc_get_sset_count(struct net_device *dev, int string_set) { switch (string_set) { case ETH_SS_STATS: return ARRAY_SIZE(netvsc_stats); default: return -EINVAL; } } static void netvsc_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct net_device_context *ndc = netdev_priv(dev); const void *nds = &ndc->eth_stats; int i; for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++) data[i] = *(unsigned long *)(nds + netvsc_stats[i].offset); } static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data) { int i; switch (stringset) { case ETH_SS_STATS: for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++) memcpy(data + i * ETH_GSTRING_LEN, netvsc_stats[i].name, ETH_GSTRING_LEN); break; } } |
316158fef hyperv: Add netpo... |
990 991 992 993 994 995 996 997 |
#ifdef CONFIG_NET_POLL_CONTROLLER static void netvsc_poll_controller(struct net_device *net) { /* As netvsc_start_xmit() works synchronous we don't have to * trigger anything here. */ } #endif |
1ce09e899 hyperv: Add suppo... |
998 |
|
f82f4ad7b Staging: hv: add ... |
999 1000 |
static const struct ethtool_ops ethtool_ops = { .get_drvinfo = netvsc_get_drvinfo, |
f82f4ad7b Staging: hv: add ... |
1001 |
.get_link = ethtool_op_get_link, |
4323b47cf hv_netvsc: add et... |
1002 1003 1004 |
.get_ethtool_stats = netvsc_get_ethtool_stats, .get_sset_count = netvsc_get_sset_count, .get_strings = netvsc_get_strings, |
59995370d hyperv: Implement... |
1005 |
.get_channels = netvsc_get_channels, |
b5960e6e2 hv_netvsc: Implem... |
1006 |
.set_channels = netvsc_set_channels, |
76d13b568 hv_netvsc: add so... |
1007 |
.get_ts_info = ethtool_op_get_ts_info, |
49eb93892 hv_netvsc: add et... |
1008 1009 |
.get_settings = netvsc_get_settings, .set_settings = netvsc_set_settings, |
f82f4ad7b Staging: hv: add ... |
1010 |
}; |
df2fff28e Staging: hv: reor... |
1011 1012 1013 1014 |
static const struct net_device_ops device_ops = { .ndo_open = netvsc_open, .ndo_stop = netvsc_close, .ndo_start_xmit = netvsc_start_xmit, |
afc4b13df net: remove use o... |
1015 |
.ndo_set_rx_mode = netvsc_set_multicast_list, |
4d447c9a6 net/hyperv: Add s... |
1016 |
.ndo_change_mtu = netvsc_change_mtu, |
b681b5886 staging: hv: Fix ... |
1017 |
.ndo_validate_addr = eth_validate_addr, |
1ce09e899 hyperv: Add suppo... |
1018 |
.ndo_set_mac_address = netvsc_set_mac_addr, |
5b54dac85 hyperv: Add suppo... |
1019 |
.ndo_select_queue = netvsc_select_queue, |
7eafd9b40 hv_netvsc: use pe... |
1020 |
.ndo_get_stats64 = netvsc_get_stats64, |
316158fef hyperv: Add netpo... |
1021 1022 1023 |
#ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = netvsc_poll_controller, #endif |
df2fff28e Staging: hv: reor... |
1024 |
}; |
c996edcf1 staging: hv: Fix ... |
1025 |
/* |
27a70af3f hv_netvsc: rework... |
1026 1027 1028 |
* Handle link status changes. For RNDIS_STATUS_NETWORK_CHANGE emulate link * down/up sequence. In case of RNDIS_STATUS_MEDIA_CONNECT when carrier is * present send GARP packet to network peers with netif_notify_peers(). |
c996edcf1 staging: hv: Fix ... |
1029 |
*/ |
891de74d6 hyperv: Fix the c... |
1030 |
static void netvsc_link_change(struct work_struct *w) |
c996edcf1 staging: hv: Fix ... |
1031 |
{ |
0a1275ca5 hv_netvsc: get ri... |
1032 1033 1034 1035 |
struct net_device_context *ndev_ctx = container_of(w, struct net_device_context, dwork.work); struct hv_device *device_obj = ndev_ctx->device_ctx; struct net_device *net = hv_get_drvdata(device_obj); |
2ddd5e5fb Staging: hv: netv... |
1036 |
struct netvsc_device *net_device; |
891de74d6 hyperv: Fix the c... |
1037 |
struct rndis_device *rdev; |
27a70af3f hv_netvsc: rework... |
1038 1039 1040 |
struct netvsc_reconfig *event = NULL; bool notify = false, reschedule = false; unsigned long flags, next_reconfig, delay; |
c996edcf1 staging: hv: Fix ... |
1041 |
|
1bdcec8a5 hv_netvsc: use st... |
1042 1043 1044 |
rtnl_lock(); if (ndev_ctx->start_remove) goto out_unlock; |
3d541ac5a hv_netvsc: untang... |
1045 |
net_device = ndev_ctx->nvdev; |
891de74d6 hyperv: Fix the c... |
1046 |
rdev = net_device->extension; |
891de74d6 hyperv: Fix the c... |
1047 |
|
27a70af3f hv_netvsc: rework... |
1048 1049 1050 1051 1052 1053 1054 1055 1056 |
next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT; if (time_is_after_jiffies(next_reconfig)) { /* link_watch only sends one notification with current state * per second, avoid doing reconfig more frequently. Handle * wrap around. */ delay = next_reconfig - jiffies; delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT; schedule_delayed_work(&ndev_ctx->dwork, delay); |
1bdcec8a5 hv_netvsc: use st... |
1057 |
goto out_unlock; |
27a70af3f hv_netvsc: rework... |
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 |
} ndev_ctx->last_reconfig = jiffies; spin_lock_irqsave(&ndev_ctx->lock, flags); if (!list_empty(&ndev_ctx->reconfig_events)) { event = list_first_entry(&ndev_ctx->reconfig_events, struct netvsc_reconfig, list); list_del(&event->list); reschedule = !list_empty(&ndev_ctx->reconfig_events); } spin_unlock_irqrestore(&ndev_ctx->lock, flags); if (!event) |
1bdcec8a5 hv_netvsc: use st... |
1071 |
goto out_unlock; |
27a70af3f hv_netvsc: rework... |
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 |
switch (event->event) { /* Only the following events are possible due to the check in * netvsc_linkstatus_callback() */ case RNDIS_STATUS_MEDIA_CONNECT: if (rdev->link_state) { rdev->link_state = false; netif_carrier_on(net); netif_tx_wake_all_queues(net); } else { notify = true; } kfree(event); break; case RNDIS_STATUS_MEDIA_DISCONNECT: if (!rdev->link_state) { rdev->link_state = true; netif_carrier_off(net); netif_tx_stop_all_queues(net); } kfree(event); break; case RNDIS_STATUS_NETWORK_CHANGE: /* Only makes sense if carrier is present */ if (!rdev->link_state) { rdev->link_state = true; netif_carrier_off(net); netif_tx_stop_all_queues(net); event->event = RNDIS_STATUS_MEDIA_CONNECT; spin_lock_irqsave(&ndev_ctx->lock, flags); |
15cfd4077 hv_netvsc: Fix th... |
1103 |
list_add(&event->list, &ndev_ctx->reconfig_events); |
27a70af3f hv_netvsc: rework... |
1104 1105 |
spin_unlock_irqrestore(&ndev_ctx->lock, flags); reschedule = true; |
3a494e710 hyperv: Add handl... |
1106 |
} |
27a70af3f hv_netvsc: rework... |
1107 |
break; |
891de74d6 hyperv: Fix the c... |
1108 1109 1110 1111 1112 1113 |
} rtnl_unlock(); if (notify) netdev_notify_peers(net); |
27a70af3f hv_netvsc: rework... |
1114 1115 1116 1117 1118 1119 |
/* link_watch only sends one notification with current state per * second, handle next reconfig event in 2 seconds. */ if (reschedule) schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT); |
1bdcec8a5 hv_netvsc: use st... |
1120 1121 1122 1123 1124 |
return; out_unlock: rtnl_unlock(); |
c996edcf1 staging: hv: Fix ... |
1125 |
} |
7eafd9b40 hv_netvsc: use pe... |
1126 1127 1128 1129 1130 1131 1132 1133 |
static void netvsc_free_netdev(struct net_device *netdev) { struct net_device_context *net_device_ctx = netdev_priv(netdev); free_percpu(net_device_ctx->tx_stats); free_percpu(net_device_ctx->rx_stats); free_netdev(netdev); } |
c996edcf1 staging: hv: Fix ... |
1134 |
|
e8ff40d4b hv_netvsc: improv... |
1135 |
static struct net_device *get_netvsc_bymac(const u8 *mac) |
84bf9cefb hv_netvsc: Implem... |
1136 |
{ |
e8ff40d4b hv_netvsc: improv... |
1137 |
struct net_device *dev; |
84bf9cefb hv_netvsc: Implem... |
1138 |
|
8737caafd hv_netvsc: fix rt... |
1139 |
ASSERT_RTNL(); |
84bf9cefb hv_netvsc: Implem... |
1140 1141 |
for_each_netdev(&init_net, dev) { |
e8ff40d4b hv_netvsc: improv... |
1142 1143 1144 1145 1146 1147 1148 1149 1150 |
if (dev->netdev_ops != &device_ops) continue; /* not a netvsc device */ if (ether_addr_equal(mac, dev->perm_addr)) return dev; } return NULL; } |
f207c10d9 hv_netvsc: use RC... |
1151 |
static struct net_device *get_netvsc_byref(struct net_device *vf_netdev) |
e8ff40d4b hv_netvsc: improv... |
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 |
{ struct net_device *dev; ASSERT_RTNL(); for_each_netdev(&init_net, dev) { struct net_device_context *net_device_ctx; if (dev->netdev_ops != &device_ops) continue; /* not a netvsc device */ net_device_ctx = netdev_priv(dev); if (net_device_ctx->nvdev == NULL) continue; /* device is removed */ |
f207c10d9 hv_netvsc: use RC... |
1166 |
if (rtnl_dereference(net_device_ctx->vf_netdev) == vf_netdev) |
e8ff40d4b hv_netvsc: improv... |
1167 |
return dev; /* a match */ |
84bf9cefb hv_netvsc: Implem... |
1168 |
} |
84bf9cefb hv_netvsc: Implem... |
1169 |
|
e8ff40d4b hv_netvsc: improv... |
1170 |
return NULL; |
84bf9cefb hv_netvsc: Implem... |
1171 1172 1173 1174 |
} static int netvsc_register_vf(struct net_device *vf_netdev) { |
0a1275ca5 hv_netvsc: get ri... |
1175 1176 |
struct net_device *ndev; struct net_device_context *net_device_ctx; |
84bf9cefb hv_netvsc: Implem... |
1177 |
struct netvsc_device *netvsc_dev; |
84bf9cefb hv_netvsc: Implem... |
1178 |
|
e8ff40d4b hv_netvsc: improv... |
1179 1180 |
if (vf_netdev->addr_len != ETH_ALEN) return NOTIFY_DONE; |
84bf9cefb hv_netvsc: Implem... |
1181 1182 1183 1184 1185 |
/* * We will use the MAC address to locate the synthetic interface to * associate with the VF interface. If we don't find a matching * synthetic interface, move on. */ |
e8ff40d4b hv_netvsc: improv... |
1186 |
ndev = get_netvsc_bymac(vf_netdev->perm_addr); |
0a1275ca5 hv_netvsc: get ri... |
1187 1188 1189 1190 1191 |
if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); netvsc_dev = net_device_ctx->nvdev; |
f207c10d9 hv_netvsc: use RC... |
1192 |
if (!netvsc_dev || rtnl_dereference(net_device_ctx->vf_netdev)) |
84bf9cefb hv_netvsc: Implem... |
1193 |
return NOTIFY_DONE; |
0a1275ca5 hv_netvsc: get ri... |
1194 1195 |
netdev_info(ndev, "VF registering: %s ", vf_netdev->name); |
84bf9cefb hv_netvsc: Implem... |
1196 1197 1198 1199 |
/* * Take a reference on the module. */ try_module_get(THIS_MODULE); |
07d0f0008 hv_netvsc: dev ho... |
1200 1201 |
dev_hold(vf_netdev); |
f207c10d9 hv_netvsc: use RC... |
1202 |
rcu_assign_pointer(net_device_ctx->vf_netdev, vf_netdev); |
84bf9cefb hv_netvsc: Implem... |
1203 1204 |
return NOTIFY_OK; } |
84bf9cefb hv_netvsc: Implem... |
1205 1206 |
static int netvsc_vf_up(struct net_device *vf_netdev) { |
0a1275ca5 hv_netvsc: get ri... |
1207 |
struct net_device *ndev; |
84bf9cefb hv_netvsc: Implem... |
1208 |
struct netvsc_device *netvsc_dev; |
84bf9cefb hv_netvsc: Implem... |
1209 |
struct net_device_context *net_device_ctx; |
e8ff40d4b hv_netvsc: improv... |
1210 |
ndev = get_netvsc_byref(vf_netdev); |
0a1275ca5 hv_netvsc: get ri... |
1211 1212 1213 1214 1215 |
if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); netvsc_dev = net_device_ctx->nvdev; |
84bf9cefb hv_netvsc: Implem... |
1216 |
|
0a1275ca5 hv_netvsc: get ri... |
1217 1218 |
netdev_info(ndev, "VF up: %s ", vf_netdev->name); |
84bf9cefb hv_netvsc: Implem... |
1219 1220 1221 1222 |
/* * Open the device before switching data path. */ |
2f5fa6c86 hv_netvsc: pass s... |
1223 |
rndis_filter_open(netvsc_dev); |
84bf9cefb hv_netvsc: Implem... |
1224 1225 1226 1227 |
/* * notify the host to switch the data path. */ |
0a1275ca5 hv_netvsc: get ri... |
1228 1229 1230 |
netvsc_switch_datapath(ndev, true); netdev_info(ndev, "Data path switched to VF: %s ", vf_netdev->name); |
84bf9cefb hv_netvsc: Implem... |
1231 |
|
0a1275ca5 hv_netvsc: get ri... |
1232 |
netif_carrier_off(ndev); |
84bf9cefb hv_netvsc: Implem... |
1233 |
|
d072218f2 hv_netvsc: avoid ... |
1234 1235 |
/* Now notify peers through VF device. */ call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, vf_netdev); |
84bf9cefb hv_netvsc: Implem... |
1236 1237 1238 |
return NOTIFY_OK; } |
84bf9cefb hv_netvsc: Implem... |
1239 1240 |
static int netvsc_vf_down(struct net_device *vf_netdev) { |
0a1275ca5 hv_netvsc: get ri... |
1241 |
struct net_device *ndev; |
84bf9cefb hv_netvsc: Implem... |
1242 1243 |
struct netvsc_device *netvsc_dev; struct net_device_context *net_device_ctx; |
84bf9cefb hv_netvsc: Implem... |
1244 |
|
e8ff40d4b hv_netvsc: improv... |
1245 |
ndev = get_netvsc_byref(vf_netdev); |
0a1275ca5 hv_netvsc: get ri... |
1246 1247 1248 1249 1250 |
if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); netvsc_dev = net_device_ctx->nvdev; |
84bf9cefb hv_netvsc: Implem... |
1251 |
|
0a1275ca5 hv_netvsc: get ri... |
1252 1253 |
netdev_info(ndev, "VF down: %s ", vf_netdev->name); |
0a1275ca5 hv_netvsc: get ri... |
1254 1255 1256 |
netvsc_switch_datapath(ndev, false); netdev_info(ndev, "Data path switched from VF: %s ", vf_netdev->name); |
2f5fa6c86 hv_netvsc: pass s... |
1257 |
rndis_filter_close(netvsc_dev); |
0a1275ca5 hv_netvsc: get ri... |
1258 |
netif_carrier_on(ndev); |
d072218f2 hv_netvsc: avoid ... |
1259 1260 1261 |
/* Now notify peers through netvsc device. */ call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, ndev); |
84bf9cefb hv_netvsc: Implem... |
1262 1263 1264 |
return NOTIFY_OK; } |
84bf9cefb hv_netvsc: Implem... |
1265 1266 |
static int netvsc_unregister_vf(struct net_device *vf_netdev) { |
0a1275ca5 hv_netvsc: get ri... |
1267 |
struct net_device *ndev; |
84bf9cefb hv_netvsc: Implem... |
1268 |
struct netvsc_device *netvsc_dev; |
0a1275ca5 hv_netvsc: get ri... |
1269 |
struct net_device_context *net_device_ctx; |
84bf9cefb hv_netvsc: Implem... |
1270 |
|
e8ff40d4b hv_netvsc: improv... |
1271 |
ndev = get_netvsc_byref(vf_netdev); |
0a1275ca5 hv_netvsc: get ri... |
1272 1273 1274 1275 1276 |
if (!ndev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); netvsc_dev = net_device_ctx->nvdev; |
e8ff40d4b hv_netvsc: improv... |
1277 |
|
0a1275ca5 hv_netvsc: get ri... |
1278 1279 |
netdev_info(ndev, "VF unregistering: %s ", vf_netdev->name); |
f207c10d9 hv_netvsc: use RC... |
1280 1281 |
RCU_INIT_POINTER(net_device_ctx->vf_netdev, NULL); |
07d0f0008 hv_netvsc: dev ho... |
1282 |
dev_put(vf_netdev); |
84bf9cefb hv_netvsc: Implem... |
1283 1284 1285 |
module_put(THIS_MODULE); return NOTIFY_OK; } |
84946899b Staging: hv: vmbu... |
1286 1287 |
static int netvsc_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) |
df2fff28e Staging: hv: reor... |
1288 |
{ |
df2fff28e Staging: hv: reor... |
1289 1290 1291 |
struct net_device *net = NULL; struct net_device_context *net_device_ctx; struct netvsc_device_info device_info; |
5b54dac85 hyperv: Add suppo... |
1292 |
struct netvsc_device *nvdev; |
df2fff28e Staging: hv: reor... |
1293 |
int ret; |
5b54dac85 hyperv: Add suppo... |
1294 1295 |
net = alloc_etherdev_mq(sizeof(struct net_device_context), num_online_cpus()); |
df2fff28e Staging: hv: reor... |
1296 |
if (!net) |
51a805d0c Staging: hv: netv... |
1297 |
return -ENOMEM; |
df2fff28e Staging: hv: reor... |
1298 |
|
1b07da516 hyperv: Move stat... |
1299 |
netif_carrier_off(net); |
b37879e6c hv_netvsc: Add qu... |
1300 |
netvsc_init_settings(net); |
df2fff28e Staging: hv: reor... |
1301 |
net_device_ctx = netdev_priv(net); |
9efd21e1f Staging: hv: Use ... |
1302 |
net_device_ctx->device_ctx = dev; |
3f300ff41 hv_netvsc: introd... |
1303 1304 1305 1306 1307 |
net_device_ctx->msg_enable = netif_msg_init(debug, default_msg); if (netif_msg_probe(net_device_ctx)) netdev_dbg(net, "netvsc msg_enable: %d ", net_device_ctx->msg_enable); |
7eafd9b40 hv_netvsc: use pe... |
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 |
net_device_ctx->tx_stats = netdev_alloc_pcpu_stats(struct netvsc_stats); if (!net_device_ctx->tx_stats) { free_netdev(net); return -ENOMEM; } net_device_ctx->rx_stats = netdev_alloc_pcpu_stats(struct netvsc_stats); if (!net_device_ctx->rx_stats) { free_percpu(net_device_ctx->tx_stats); free_netdev(net); return -ENOMEM; } |
2ddd5e5fb Staging: hv: netv... |
1319 |
hv_set_drvdata(dev, net); |
f580aec4b hv_netvsc: move s... |
1320 1321 |
net_device_ctx->start_remove = false; |
891de74d6 hyperv: Fix the c... |
1322 |
INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change); |
792df8722 net/hyperv: Addin... |
1323 |
INIT_WORK(&net_device_ctx->work, do_set_multicast); |
df2fff28e Staging: hv: reor... |
1324 |
|
27a70af3f hv_netvsc: rework... |
1325 1326 |
spin_lock_init(&net_device_ctx->lock); INIT_LIST_HEAD(&net_device_ctx->reconfig_events); |
df2fff28e Staging: hv: reor... |
1327 |
net->netdev_ops = &device_ops; |
a060679c6 hv_netvsc: cleanu... |
1328 1329 |
net->hw_features = NETVSC_HW_FEATURES; net->features = NETVSC_HW_FEATURES | NETIF_F_HW_VLAN_CTAG_TX; |
6048718d7 Staging: hv: tran... |
1330 |
|
7ad24ea4b net: get rid of S... |
1331 |
net->ethtool_ops = ðtool_ops; |
9efd21e1f Staging: hv: Use ... |
1332 |
SET_NETDEV_DEV(net, &dev->device); |
df2fff28e Staging: hv: reor... |
1333 |
|
14a03cf80 hv_netvsc: Restor... |
1334 1335 |
/* We always need headroom for rndis header */ net->needed_headroom = RNDIS_AND_PPI_SIZE; |
692e084e7 staging: hv: re-o... |
1336 |
/* Notify the netvsc driver of the new device */ |
8ebdcc52b hv_netvsc: Set vR... |
1337 |
memset(&device_info, 0, sizeof(device_info)); |
692e084e7 staging: hv: re-o... |
1338 |
device_info.ring_size = ring_size; |
e01ec2199 hv_netvsc: Proper... |
1339 |
device_info.max_num_vrss_chns = max_num_vrss_chns; |
692e084e7 staging: hv: re-o... |
1340 1341 1342 1343 |
ret = rndis_filter_device_add(dev, &device_info); if (ret != 0) { netdev_err(net, "unable to add netvsc device (ret %d) ", ret); |
7eafd9b40 hv_netvsc: use pe... |
1344 |
netvsc_free_netdev(net); |
2ddd5e5fb Staging: hv: netv... |
1345 |
hv_set_drvdata(dev, NULL); |
692e084e7 staging: hv: re-o... |
1346 |
return ret; |
df2fff28e Staging: hv: reor... |
1347 |
} |
692e084e7 staging: hv: re-o... |
1348 |
memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); |
3d541ac5a hv_netvsc: untang... |
1349 |
nvdev = net_device_ctx->nvdev; |
5b54dac85 hyperv: Add suppo... |
1350 1351 |
netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); |
a50af86dd netvsc: reduce ma... |
1352 |
netif_set_gso_max_size(net, NETVSC_GSO_MAX_SIZE); |
5b54dac85 hyperv: Add suppo... |
1353 |
|
a68f96146 hyperv: Fix race ... |
1354 1355 1356 1357 1358 |
ret = register_netdev(net); if (ret != 0) { pr_err("Unable to register netdev. "); rndis_filter_device_remove(dev); |
7eafd9b40 hv_netvsc: use pe... |
1359 |
netvsc_free_netdev(net); |
a68f96146 hyperv: Fix race ... |
1360 |
} |
df2fff28e Staging: hv: reor... |
1361 1362 |
return ret; } |
415b023ab Staging: hv: Use ... |
1363 |
static int netvsc_remove(struct hv_device *dev) |
df2fff28e Staging: hv: reor... |
1364 |
{ |
2ddd5e5fb Staging: hv: netv... |
1365 |
struct net_device *net; |
122a5f641 staging: hv: use ... |
1366 |
struct net_device_context *ndev_ctx; |
2ddd5e5fb Staging: hv: netv... |
1367 |
struct netvsc_device *net_device; |
3d541ac5a hv_netvsc: untang... |
1368 |
net = hv_get_drvdata(dev); |
df2fff28e Staging: hv: reor... |
1369 |
|
df2fff28e Staging: hv: reor... |
1370 |
if (net == NULL) { |
415b023ab Staging: hv: Use ... |
1371 1372 |
dev_err(&dev->device, "No net device to remove "); |
df2fff28e Staging: hv: reor... |
1373 1374 |
return 0; } |
122a5f641 staging: hv: use ... |
1375 |
ndev_ctx = netdev_priv(net); |
3d541ac5a hv_netvsc: untang... |
1376 |
net_device = ndev_ctx->nvdev; |
6da7225f5 hv_netvsc: synchr... |
1377 1378 1379 1380 |
/* Avoid racing with netvsc_change_mtu()/netvsc_set_channels() * removing the device. */ rtnl_lock(); |
f580aec4b hv_netvsc: move s... |
1381 |
ndev_ctx->start_remove = true; |
6da7225f5 hv_netvsc: synchr... |
1382 |
rtnl_unlock(); |
f580aec4b hv_netvsc: move s... |
1383 |
|
122a5f641 staging: hv: use ... |
1384 |
cancel_delayed_work_sync(&ndev_ctx->dwork); |
792df8722 net/hyperv: Addin... |
1385 |
cancel_work_sync(&ndev_ctx->work); |
122a5f641 staging: hv: use ... |
1386 |
|
df2fff28e Staging: hv: reor... |
1387 |
/* Stop outbound asap */ |
0a282538c net/hyperv: Use n... |
1388 |
netif_tx_disable(net); |
df2fff28e Staging: hv: reor... |
1389 1390 1391 1392 1393 1394 1395 |
unregister_netdev(net); /* * Call to the vsc driver to let it know that the device is being * removed */ |
df06bcff8 staging: hv: chan... |
1396 |
rndis_filter_device_remove(dev); |
df2fff28e Staging: hv: reor... |
1397 |
|
3d541ac5a hv_netvsc: untang... |
1398 |
hv_set_drvdata(dev, NULL); |
7eafd9b40 hv_netvsc: use pe... |
1399 |
netvsc_free_netdev(net); |
df06bcff8 staging: hv: chan... |
1400 |
return 0; |
df2fff28e Staging: hv: reor... |
1401 |
} |
345c4cc3b Staging: hv: netv... |
1402 |
static const struct hv_vmbus_device_id id_table[] = { |
c45cf2d49 Staging: hv: crea... |
1403 |
/* Network guid */ |
8f5059446 Drivers: net: hyp... |
1404 |
{ HV_NIC_GUID, }, |
c45cf2d49 Staging: hv: crea... |
1405 |
{ }, |
345c4cc3b Staging: hv: netv... |
1406 1407 1408 |
}; MODULE_DEVICE_TABLE(vmbus, id_table); |
f1542a660 Staging: hv: Move... |
1409 |
/* The one and only one */ |
fde0ef9b2 Staging: hv: netv... |
1410 |
static struct hv_driver netvsc_drv = { |
d31b20fcc net/hyperv: Use t... |
1411 |
.name = KBUILD_MODNAME, |
345c4cc3b Staging: hv: netv... |
1412 |
.id_table = id_table, |
fde0ef9b2 Staging: hv: netv... |
1413 1414 |
.probe = netvsc_probe, .remove = netvsc_remove, |
d48909703 Staging: hv: netv... |
1415 |
}; |
f1542a660 Staging: hv: Move... |
1416 |
|
84bf9cefb hv_netvsc: Implem... |
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 |
/* * On Hyper-V, every VF interface is matched with a corresponding * synthetic interface. The synthetic interface is presented first * to the guest. When the corresponding VF instance is registered, * we will take care of switching the data path. */ static int netvsc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); |
ee837a137 hv_netvsc: simpli... |
1427 1428 1429 1430 1431 1432 1433 |
/* Skip our own events */ if (event_dev->netdev_ops == &device_ops) return NOTIFY_DONE; /* Avoid non-Ethernet type devices */ if (event_dev->type != ARPHRD_ETHER) return NOTIFY_DONE; |
0dbff144a hv_netvsc: fix bo... |
1434 1435 1436 1437 1438 |
/* Avoid Vlan dev with same MAC registering as VF */ if (event_dev->priv_flags & IFF_802_1Q_VLAN) return NOTIFY_DONE; /* Avoid Bonding master dev with same MAC registering as VF */ |
ee837a137 hv_netvsc: simpli... |
1439 1440 |
if ((event_dev->priv_flags & IFF_BONDING) && (event_dev->flags & IFF_MASTER)) |
cb2911fed hv_netvsc: Fix VF... |
1441 |
return NOTIFY_DONE; |
84bf9cefb hv_netvsc: Implem... |
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 |
switch (event) { case NETDEV_REGISTER: return netvsc_register_vf(event_dev); case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: return netvsc_vf_up(event_dev); case NETDEV_DOWN: return netvsc_vf_down(event_dev); default: return NOTIFY_DONE; } } static struct notifier_block netvsc_netdev_notifier = { .notifier_call = netvsc_netdev_event, }; |
a9869c942 Staging: hv: netv... |
1459 |
static void __exit netvsc_drv_exit(void) |
fceaf24a9 Staging: hv: add ... |
1460 |
{ |
84bf9cefb hv_netvsc: Implem... |
1461 |
unregister_netdevice_notifier(&netvsc_netdev_notifier); |
768fa2191 Staging: hv: fix ... |
1462 |
vmbus_driver_unregister(&netvsc_drv); |
fceaf24a9 Staging: hv: add ... |
1463 |
} |
1fde28cff Staging: hv: netv... |
1464 |
static int __init netvsc_drv_init(void) |
df2fff28e Staging: hv: reor... |
1465 |
{ |
84bf9cefb hv_netvsc: Implem... |
1466 |
int ret; |
fa85a6c29 hyperv: Add a che... |
1467 1468 1469 1470 1471 1472 |
if (ring_size < RING_SIZE_MIN) { ring_size = RING_SIZE_MIN; pr_info("Increased ring_size to %d (min allowed) ", ring_size); } |
84bf9cefb hv_netvsc: Implem... |
1473 1474 1475 1476 1477 1478 1479 |
ret = vmbus_driver_register(&netvsc_drv); if (ret) return ret; register_netdevice_notifier(&netvsc_netdev_notifier); return 0; |
df2fff28e Staging: hv: reor... |
1480 |
} |
26c14cc11 Staging: hv: Add ... |
1481 |
MODULE_LICENSE("GPL"); |
7880fc54c Staging: hv: clea... |
1482 |
MODULE_DESCRIPTION("Microsoft Hyper-V network driver"); |
fceaf24a9 Staging: hv: add ... |
1483 |
|
1fde28cff Staging: hv: netv... |
1484 |
module_init(netvsc_drv_init); |
a9869c942 Staging: hv: netv... |
1485 |
module_exit(netvsc_drv_exit); |