aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/hyperv
diff options
context:
space:
mode:
authorHaiyang Zhang <haiyangz@microsoft.com>2016-01-25 09:49:31 -0800
committerDavid S. Miller <davem@davemloft.net>2016-01-25 10:51:53 -0800
commitc85e4924452ae8225c8829f3fa8a2f7baa34bc5c (patch)
tree1c7fe6024c71a212c6c71d45ed695b842b8357eb /drivers/net/hyperv
parenthv_netvsc: use skb_get_hash() instead of a homegrown implementation (diff)
downloadlinux-dev-c85e4924452ae8225c8829f3fa8a2f7baa34bc5c.tar.xz
linux-dev-c85e4924452ae8225c8829f3fa8a2f7baa34bc5c.zip
hv_netvsc: Fix book keeping of skb during batching process
Since eliminating send_completion_tid from struct hv_netvsc_packet, we haven't add proper book keeping for the skb of the batched packet. This patch fixes this issue and allows the previous skb is properly freed. Otherwise, a panic may happen. Thanks to Simon Xiao <sixiao@microsoft.com> for bisecting and analysis. Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com> Reviewed-by: K. Y. Srinivasan <kys@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/hyperv')
-rw-r--r--drivers/net/hyperv/hyperv_net.h1
-rw-r--r--drivers/net/hyperv/netvsc.c33
2 files changed, 23 insertions, 11 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index f4130af09244..fcb92c0d0eb9 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -624,6 +624,7 @@ struct nvsp_message {
#define RNDIS_PKT_ALIGN_DEFAULT 8
struct multi_send_data {
+ struct sk_buff *skb; /* skb containing the pkt */
struct hv_netvsc_packet *pkt; /* netvsc pkt pending */
u32 count; /* counter of batched packets */
};
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 059fc5231601..ec313fc08d82 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -841,6 +841,18 @@ static inline int netvsc_send_pkt(
return ret;
}
+/* Move packet out of multi send data (msd), and clear msd */
+static inline void move_pkt_msd(struct hv_netvsc_packet **msd_send,
+ struct sk_buff **msd_skb,
+ struct multi_send_data *msdp)
+{
+ *msd_skb = msdp->skb;
+ *msd_send = msdp->pkt;
+ msdp->skb = NULL;
+ msdp->pkt = NULL;
+ msdp->count = 0;
+}
+
int netvsc_send(struct hv_device *device,
struct hv_netvsc_packet *packet,
struct rndis_message *rndis_msg,
@@ -855,6 +867,7 @@ int netvsc_send(struct hv_device *device,
unsigned int section_index = NETVSC_INVALID_INDEX;
struct multi_send_data *msdp;
struct hv_netvsc_packet *msd_send = NULL, *cur_send = NULL;
+ struct sk_buff *msd_skb = NULL;
bool try_batch;
bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
@@ -897,10 +910,8 @@ int netvsc_send(struct hv_device *device,
net_device->send_section_size) {
section_index = netvsc_get_next_send_section(net_device);
if (section_index != NETVSC_INVALID_INDEX) {
- msd_send = msdp->pkt;
- msdp->pkt = NULL;
- msdp->count = 0;
- msd_len = 0;
+ move_pkt_msd(&msd_send, &msd_skb, msdp);
+ msd_len = 0;
}
}
@@ -919,31 +930,31 @@ int netvsc_send(struct hv_device *device,
packet->total_data_buflen += msd_len;
}
- if (msdp->pkt)
- dev_kfree_skb_any(skb);
+ if (msdp->skb)
+ dev_kfree_skb_any(msdp->skb);
if (xmit_more && !packet->cp_partial) {
+ msdp->skb = skb;
msdp->pkt = packet;
msdp->count++;
} else {
cur_send = packet;
+ msdp->skb = NULL;
msdp->pkt = NULL;
msdp->count = 0;
}
} else {
- msd_send = msdp->pkt;
- msdp->pkt = NULL;
- msdp->count = 0;
+ move_pkt_msd(&msd_send, &msd_skb, msdp);
cur_send = packet;
}
if (msd_send) {
- m_ret = netvsc_send_pkt(msd_send, net_device, pb, skb);
+ m_ret = netvsc_send_pkt(msd_send, net_device, NULL, msd_skb);
if (m_ret != 0) {
netvsc_free_send_slot(net_device,
msd_send->send_buf_index);
- dev_kfree_skb_any(skb);
+ dev_kfree_skb_any(msd_skb);
}
}