|
@@ -342,7 +342,8 @@ static void rndis_filter_receive_response(struct net_device *ndev,
|
|
|
* Get the Per-Packet-Info with the specified type
|
|
|
* return NULL if not found.
|
|
|
*/
|
|
|
-static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
|
|
|
+static inline void *rndis_get_ppi(struct rndis_packet *rpkt,
|
|
|
+ u32 type, u8 internal)
|
|
|
{
|
|
|
struct rndis_per_packet_info *ppi;
|
|
|
int len;
|
|
@@ -355,7 +356,7 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
|
|
|
len = rpkt->per_pkt_info_len;
|
|
|
|
|
|
while (len > 0) {
|
|
|
- if (ppi->type == type)
|
|
|
+ if (ppi->type == type && ppi->internal == internal)
|
|
|
return (void *)((ulong)ppi + ppi->ppi_offset);
|
|
|
len -= ppi->size;
|
|
|
ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
|
|
@@ -364,17 +365,41 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+static inline
|
|
|
+void rsc_add_data(struct netvsc_channel *nvchan,
|
|
|
+ const struct ndis_pkt_8021q_info *vlan,
|
|
|
+ const struct ndis_tcp_ip_checksum_info *csum_info,
|
|
|
+ void *data, u32 len)
|
|
|
+{
|
|
|
+ u32 cnt = nvchan->rsc.cnt;
|
|
|
+
|
|
|
+ if (cnt) {
|
|
|
+ nvchan->rsc.pktlen += len;
|
|
|
+ } else {
|
|
|
+ nvchan->rsc.vlan = vlan;
|
|
|
+ nvchan->rsc.csum_info = csum_info;
|
|
|
+ nvchan->rsc.pktlen = len;
|
|
|
+ }
|
|
|
+
|
|
|
+ nvchan->rsc.data[cnt] = data;
|
|
|
+ nvchan->rsc.len[cnt] = len;
|
|
|
+ nvchan->rsc.cnt++;
|
|
|
+}
|
|
|
+
|
|
|
static int rndis_filter_receive_data(struct net_device *ndev,
|
|
|
struct netvsc_device *nvdev,
|
|
|
- struct vmbus_channel *channel,
|
|
|
+ struct netvsc_channel *nvchan,
|
|
|
struct rndis_message *msg,
|
|
|
u32 data_buflen)
|
|
|
{
|
|
|
struct rndis_packet *rndis_pkt = &msg->msg.pkt;
|
|
|
const struct ndis_tcp_ip_checksum_info *csum_info;
|
|
|
const struct ndis_pkt_8021q_info *vlan;
|
|
|
+ const struct rndis_pktinfo_id *pktinfo_id;
|
|
|
u32 data_offset;
|
|
|
void *data;
|
|
|
+ bool rsc_more = false;
|
|
|
+ int ret;
|
|
|
|
|
|
/* Remove the rndis header and pass it back up the stack */
|
|
|
data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
|
|
@@ -393,25 +418,59 @@ static int rndis_filter_receive_data(struct net_device *ndev,
|
|
|
return NVSP_STAT_FAIL;
|
|
|
}
|
|
|
|
|
|
- vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
|
|
|
+ vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO, 0);
|
|
|
+
|
|
|
+ csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO, 0);
|
|
|
|
|
|
- csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
|
|
|
+ pktinfo_id = rndis_get_ppi(rndis_pkt, RNDIS_PKTINFO_ID, 1);
|
|
|
|
|
|
data = (void *)msg + data_offset;
|
|
|
|
|
|
- /*
|
|
|
- * Remove the rndis trailer padding from rndis packet message
|
|
|
+ /* Identify RSC frags, drop erroneous packets */
|
|
|
+ if (pktinfo_id && (pktinfo_id->flag & RNDIS_PKTINFO_SUBALLOC)) {
|
|
|
+ if (pktinfo_id->flag & RNDIS_PKTINFO_1ST_FRAG)
|
|
|
+ nvchan->rsc.cnt = 0;
|
|
|
+ else if (nvchan->rsc.cnt == 0)
|
|
|
+ goto drop;
|
|
|
+
|
|
|
+ rsc_more = true;
|
|
|
+
|
|
|
+ if (pktinfo_id->flag & RNDIS_PKTINFO_LAST_FRAG)
|
|
|
+ rsc_more = false;
|
|
|
+
|
|
|
+ if (rsc_more && nvchan->rsc.is_last)
|
|
|
+ goto drop;
|
|
|
+ } else {
|
|
|
+ nvchan->rsc.cnt = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (unlikely(nvchan->rsc.cnt >= NVSP_RSC_MAX))
|
|
|
+ goto drop;
|
|
|
+
|
|
|
+ /* Put data into per channel structure.
|
|
|
+ * Also, remove the rndis trailer padding from rndis packet message
|
|
|
* rndis_pkt->data_len tell us the real data length, we only copy
|
|
|
* the data packet to the stack, without the rndis trailer padding
|
|
|
*/
|
|
|
- return netvsc_recv_callback(ndev, nvdev, channel,
|
|
|
- data, rndis_pkt->data_len,
|
|
|
- csum_info, vlan);
|
|
|
+ rsc_add_data(nvchan, vlan, csum_info, data, rndis_pkt->data_len);
|
|
|
+
|
|
|
+ if (rsc_more)
|
|
|
+ return NVSP_STAT_SUCCESS;
|
|
|
+
|
|
|
+ ret = netvsc_recv_callback(ndev, nvdev, nvchan);
|
|
|
+ nvchan->rsc.cnt = 0;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+
|
|
|
+drop:
|
|
|
+ /* Drop incomplete packet */
|
|
|
+ nvchan->rsc.cnt = 0;
|
|
|
+ return NVSP_STAT_FAIL;
|
|
|
}
|
|
|
|
|
|
int rndis_filter_receive(struct net_device *ndev,
|
|
|
struct netvsc_device *net_dev,
|
|
|
- struct vmbus_channel *channel,
|
|
|
+ struct netvsc_channel *nvchan,
|
|
|
void *data, u32 buflen)
|
|
|
{
|
|
|
struct net_device_context *net_device_ctx = netdev_priv(ndev);
|
|
@@ -422,7 +481,7 @@ int rndis_filter_receive(struct net_device *ndev,
|
|
|
|
|
|
switch (rndis_msg->ndis_msg_type) {
|
|
|
case RNDIS_MSG_PACKET:
|
|
|
- return rndis_filter_receive_data(ndev, net_dev, channel,
|
|
|
+ return rndis_filter_receive_data(ndev, net_dev, nvchan,
|
|
|
rndis_msg, buflen);
|
|
|
case RNDIS_MSG_INIT_C:
|
|
|
case RNDIS_MSG_QUERY_C:
|
|
@@ -657,7 +716,7 @@ cleanup:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
+int
|
|
|
rndis_filter_set_offload_params(struct net_device *ndev,
|
|
|
struct netvsc_device *nvdev,
|
|
|
struct ndis_offload_params *req_offloads)
|
|
@@ -1184,6 +1243,18 @@ static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (hwcaps.rsc.ip4 && hwcaps.rsc.ip6) {
|
|
|
+ net->hw_features |= NETIF_F_LRO;
|
|
|
+
|
|
|
+ if (net->features & NETIF_F_LRO) {
|
|
|
+ offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
|
|
|
+ offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
|
|
|
+ } else {
|
|
|
+ offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED;
|
|
|
+ offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* In case some hw_features disappeared we need to remove them from
|
|
|
* net->features list as they're no longer supported.
|
|
|
*/
|