diff options
-rw-r--r-- | drivers/net/vmxnet3/vmxnet3_defs.h | 38 | ||||
-rw-r--r-- | drivers/net/vmxnet3/vmxnet3_drv.c | 98 | ||||
-rw-r--r-- | drivers/net/vmxnet3/vmxnet3_int.h | 4 |
3 files changed, 136 insertions, 4 deletions
diff --git a/drivers/net/vmxnet3/vmxnet3_defs.h b/drivers/net/vmxnet3/vmxnet3_defs.h index 3718d024f638..221a53025fd0 100644 --- a/drivers/net/vmxnet3/vmxnet3_defs.h +++ b/drivers/net/vmxnet3/vmxnet3_defs.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Linux driver for VMware's vmxnet3 ethernet NIC. | 2 | * Linux driver for VMware's vmxnet3 ethernet NIC. |
3 | * | 3 | * |
4 | * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. | 4 | * Copyright (C) 2008-2015, VMware, Inc. All Rights Reserved. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | 7 | * under the terms of the GNU General Public License as published by the |
@@ -277,6 +277,40 @@ struct Vmxnet3_RxCompDesc { | |||
277 | #endif /* __BIG_ENDIAN_BITFIELD */ | 277 | #endif /* __BIG_ENDIAN_BITFIELD */ |
278 | }; | 278 | }; |
279 | 279 | ||
280 | struct Vmxnet3_RxCompDescExt { | ||
281 | __le32 dword1; | ||
282 | u8 segCnt; /* Number of aggregated packets */ | ||
283 | u8 dupAckCnt; /* Number of duplicate Acks */ | ||
284 | __le16 tsDelta; /* TCP timestamp difference */ | ||
285 | __le32 dword2; | ||
286 | #ifdef __BIG_ENDIAN_BITFIELD | ||
287 | u32 gen:1; /* generation bit */ | ||
288 | u32 type:7; /* completion type */ | ||
289 | u32 fcs:1; /* Frame CRC correct */ | ||
290 | u32 frg:1; /* IP Fragment */ | ||
291 | u32 v4:1; /* IPv4 */ | ||
292 | u32 v6:1; /* IPv6 */ | ||
293 | u32 ipc:1; /* IP Checksum Correct */ | ||
294 | u32 tcp:1; /* TCP packet */ | ||
295 | u32 udp:1; /* UDP packet */ | ||
296 | u32 tuc:1; /* TCP/UDP Checksum Correct */ | ||
297 | u32 mss:16; | ||
298 | #else | ||
299 | u32 mss:16; | ||
300 | u32 tuc:1; /* TCP/UDP Checksum Correct */ | ||
301 | u32 udp:1; /* UDP packet */ | ||
302 | u32 tcp:1; /* TCP packet */ | ||
303 | u32 ipc:1; /* IP Checksum Correct */ | ||
304 | u32 v6:1; /* IPv6 */ | ||
305 | u32 v4:1; /* IPv4 */ | ||
306 | u32 frg:1; /* IP Fragment */ | ||
307 | u32 fcs:1; /* Frame CRC correct */ | ||
308 | u32 type:7; /* completion type */ | ||
309 | u32 gen:1; /* generation bit */ | ||
310 | #endif /* __BIG_ENDIAN_BITFIELD */ | ||
311 | }; | ||
312 | |||
313 | |||
280 | /* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */ | 314 | /* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */ |
281 | #define VMXNET3_RCD_TUC_SHIFT 16 | 315 | #define VMXNET3_RCD_TUC_SHIFT 16 |
282 | #define VMXNET3_RCD_IPC_SHIFT 19 | 316 | #define VMXNET3_RCD_IPC_SHIFT 19 |
@@ -310,6 +344,7 @@ union Vmxnet3_GenericDesc { | |||
310 | struct Vmxnet3_RxDesc rxd; | 344 | struct Vmxnet3_RxDesc rxd; |
311 | struct Vmxnet3_TxCompDesc tcd; | 345 | struct Vmxnet3_TxCompDesc tcd; |
312 | struct Vmxnet3_RxCompDesc rcd; | 346 | struct Vmxnet3_RxCompDesc rcd; |
347 | struct Vmxnet3_RxCompDescExt rcdExt; | ||
313 | }; | 348 | }; |
314 | 349 | ||
315 | #define VMXNET3_INIT_GEN 1 | 350 | #define VMXNET3_INIT_GEN 1 |
@@ -361,6 +396,7 @@ enum { | |||
361 | /* completion descriptor types */ | 396 | /* completion descriptor types */ |
362 | #define VMXNET3_CDTYPE_TXCOMP 0 /* Tx Completion Descriptor */ | 397 | #define VMXNET3_CDTYPE_TXCOMP 0 /* Tx Completion Descriptor */ |
363 | #define VMXNET3_CDTYPE_RXCOMP 3 /* Rx Completion Descriptor */ | 398 | #define VMXNET3_CDTYPE_RXCOMP 3 /* Rx Completion Descriptor */ |
399 | #define VMXNET3_CDTYPE_RXCOMP_LRO 4 /* Rx Completion Descriptor for LRO */ | ||
364 | 400 | ||
365 | enum { | 401 | enum { |
366 | VMXNET3_GOS_BITS_UNK = 0, /* unknown */ | 402 | VMXNET3_GOS_BITS_UNK = 0, /* unknown */ |
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index ab539758bec1..da11bb5e9c7f 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c | |||
@@ -1163,6 +1163,52 @@ vmxnet3_rx_error(struct vmxnet3_rx_queue *rq, struct Vmxnet3_RxCompDesc *rcd, | |||
1163 | } | 1163 | } |
1164 | 1164 | ||
1165 | 1165 | ||
1166 | static u32 | ||
1167 | vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, | ||
1168 | union Vmxnet3_GenericDesc *gdesc) | ||
1169 | { | ||
1170 | u32 hlen, maplen; | ||
1171 | union { | ||
1172 | void *ptr; | ||
1173 | struct ethhdr *eth; | ||
1174 | struct iphdr *ipv4; | ||
1175 | struct ipv6hdr *ipv6; | ||
1176 | struct tcphdr *tcp; | ||
1177 | } hdr; | ||
1178 | BUG_ON(gdesc->rcd.tcp == 0); | ||
1179 | |||
1180 | maplen = skb_headlen(skb); | ||
1181 | if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen)) | ||
1182 | return 0; | ||
1183 | |||
1184 | hdr.eth = eth_hdr(skb); | ||
1185 | if (gdesc->rcd.v4) { | ||
1186 | BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP)); | ||
1187 | hdr.ptr += sizeof(struct ethhdr); | ||
1188 | BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP); | ||
1189 | hlen = hdr.ipv4->ihl << 2; | ||
1190 | hdr.ptr += hdr.ipv4->ihl << 2; | ||
1191 | } else if (gdesc->rcd.v6) { | ||
1192 | BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6)); | ||
1193 | hdr.ptr += sizeof(struct ethhdr); | ||
1194 | /* Use an estimated value, since we also need to handle | ||
1195 | * TSO case. | ||
1196 | */ | ||
1197 | if (hdr.ipv6->nexthdr != IPPROTO_TCP) | ||
1198 | return sizeof(struct ipv6hdr) + sizeof(struct tcphdr); | ||
1199 | hlen = sizeof(struct ipv6hdr); | ||
1200 | hdr.ptr += sizeof(struct ipv6hdr); | ||
1201 | } else { | ||
1202 | /* Non-IP pkt, dont estimate header length */ | ||
1203 | return 0; | ||
1204 | } | ||
1205 | |||
1206 | if (hlen + sizeof(struct tcphdr) > maplen) | ||
1207 | return 0; | ||
1208 | |||
1209 | return (hlen + (hdr.tcp->doff << 2)); | ||
1210 | } | ||
1211 | |||
1166 | static int | 1212 | static int |
1167 | vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, | 1213 | vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, |
1168 | struct vmxnet3_adapter *adapter, int quota) | 1214 | struct vmxnet3_adapter *adapter, int quota) |
@@ -1174,6 +1220,7 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, | |||
1174 | bool skip_page_frags = false; | 1220 | bool skip_page_frags = false; |
1175 | struct Vmxnet3_RxCompDesc *rcd; | 1221 | struct Vmxnet3_RxCompDesc *rcd; |
1176 | struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx; | 1222 | struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx; |
1223 | u16 segCnt = 0, mss = 0; | ||
1177 | #ifdef __BIG_ENDIAN_BITFIELD | 1224 | #ifdef __BIG_ENDIAN_BITFIELD |
1178 | struct Vmxnet3_RxDesc rxCmdDesc; | 1225 | struct Vmxnet3_RxDesc rxCmdDesc; |
1179 | struct Vmxnet3_RxCompDesc rxComp; | 1226 | struct Vmxnet3_RxCompDesc rxComp; |
@@ -1262,7 +1309,19 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, | |||
1262 | PCI_DMA_FROMDEVICE); | 1309 | PCI_DMA_FROMDEVICE); |
1263 | rxd->addr = cpu_to_le64(rbi->dma_addr); | 1310 | rxd->addr = cpu_to_le64(rbi->dma_addr); |
1264 | rxd->len = rbi->len; | 1311 | rxd->len = rbi->len; |
1265 | 1312 | if (adapter->version == 2 && | |
1313 | rcd->type == VMXNET3_CDTYPE_RXCOMP_LRO) { | ||
1314 | struct Vmxnet3_RxCompDescExt *rcdlro; | ||
1315 | rcdlro = (struct Vmxnet3_RxCompDescExt *)rcd; | ||
1316 | |||
1317 | segCnt = rcdlro->segCnt; | ||
1318 | BUG_ON(segCnt <= 1); | ||
1319 | mss = rcdlro->mss; | ||
1320 | if (unlikely(segCnt <= 1)) | ||
1321 | segCnt = 0; | ||
1322 | } else { | ||
1323 | segCnt = 0; | ||
1324 | } | ||
1266 | } else { | 1325 | } else { |
1267 | BUG_ON(ctx->skb == NULL && !skip_page_frags); | 1326 | BUG_ON(ctx->skb == NULL && !skip_page_frags); |
1268 | 1327 | ||
@@ -1311,12 +1370,40 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, | |||
1311 | 1370 | ||
1312 | skb = ctx->skb; | 1371 | skb = ctx->skb; |
1313 | if (rcd->eop) { | 1372 | if (rcd->eop) { |
1373 | u32 mtu = adapter->netdev->mtu; | ||
1314 | skb->len += skb->data_len; | 1374 | skb->len += skb->data_len; |
1315 | 1375 | ||
1316 | vmxnet3_rx_csum(adapter, skb, | 1376 | vmxnet3_rx_csum(adapter, skb, |
1317 | (union Vmxnet3_GenericDesc *)rcd); | 1377 | (union Vmxnet3_GenericDesc *)rcd); |
1318 | skb->protocol = eth_type_trans(skb, adapter->netdev); | 1378 | skb->protocol = eth_type_trans(skb, adapter->netdev); |
1319 | 1379 | if (!rcd->tcp || !adapter->lro) | |
1380 | goto not_lro; | ||
1381 | |||
1382 | if (segCnt != 0 && mss != 0) { | ||
1383 | skb_shinfo(skb)->gso_type = rcd->v4 ? | ||
1384 | SKB_GSO_TCPV4 : SKB_GSO_TCPV6; | ||
1385 | skb_shinfo(skb)->gso_size = mss; | ||
1386 | skb_shinfo(skb)->gso_segs = segCnt; | ||
1387 | } else if (segCnt != 0 || skb->len > mtu) { | ||
1388 | u32 hlen; | ||
1389 | |||
1390 | hlen = vmxnet3_get_hdr_len(adapter, skb, | ||
1391 | (union Vmxnet3_GenericDesc *)rcd); | ||
1392 | if (hlen == 0) | ||
1393 | goto not_lro; | ||
1394 | |||
1395 | skb_shinfo(skb)->gso_type = | ||
1396 | rcd->v4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6; | ||
1397 | if (segCnt != 0) { | ||
1398 | skb_shinfo(skb)->gso_segs = segCnt; | ||
1399 | skb_shinfo(skb)->gso_size = | ||
1400 | DIV_ROUND_UP(skb->len - | ||
1401 | hlen, segCnt); | ||
1402 | } else { | ||
1403 | skb_shinfo(skb)->gso_size = mtu - hlen; | ||
1404 | } | ||
1405 | } | ||
1406 | not_lro: | ||
1320 | if (unlikely(rcd->ts)) | 1407 | if (unlikely(rcd->ts)) |
1321 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rcd->tci); | 1408 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rcd->tci); |
1322 | 1409 | ||
@@ -3041,14 +3128,19 @@ vmxnet3_probe_device(struct pci_dev *pdev, | |||
3041 | goto err_alloc_pci; | 3128 | goto err_alloc_pci; |
3042 | 3129 | ||
3043 | ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); | 3130 | ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); |
3044 | if (ver & 1) { | 3131 | if (ver & 2) { |
3132 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 2); | ||
3133 | adapter->version = 2; | ||
3134 | } else if (ver & 1) { | ||
3045 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 1); | 3135 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 1); |
3136 | adapter->version = 1; | ||
3046 | } else { | 3137 | } else { |
3047 | dev_err(&pdev->dev, | 3138 | dev_err(&pdev->dev, |
3048 | "Incompatible h/w version (0x%x) for adapter\n", ver); | 3139 | "Incompatible h/w version (0x%x) for adapter\n", ver); |
3049 | err = -EBUSY; | 3140 | err = -EBUSY; |
3050 | goto err_ver; | 3141 | goto err_ver; |
3051 | } | 3142 | } |
3143 | dev_dbg(&pdev->dev, "Using device version %d\n", adapter->version); | ||
3052 | 3144 | ||
3053 | ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); | 3145 | ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); |
3054 | if (ver & 1) { | 3146 | if (ver & 1) { |
diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index 6bb769ae7de9..e9f1075f7d4c 100644 --- a/drivers/net/vmxnet3/vmxnet3_int.h +++ b/drivers/net/vmxnet3/vmxnet3_int.h | |||
@@ -328,6 +328,10 @@ struct vmxnet3_adapter { | |||
328 | 328 | ||
329 | u8 __iomem *hw_addr0; /* for BAR 0 */ | 329 | u8 __iomem *hw_addr0; /* for BAR 0 */ |
330 | u8 __iomem *hw_addr1; /* for BAR 1 */ | 330 | u8 __iomem *hw_addr1; /* for BAR 1 */ |
331 | u8 version; | ||
332 | |||
333 | bool rxcsum; | ||
334 | bool lro; | ||
331 | 335 | ||
332 | #ifdef VMXNET3_RSS | 336 | #ifdef VMXNET3_RSS |
333 | struct UPT1_RSSConf *rss_conf; | 337 | struct UPT1_RSSConf *rss_conf; |