diff options
author | Haiyang Zhang <haiyangz@microsoft.com> | 2016-01-25 18:49:31 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-01-25 19:51:53 +0100 |
commit | c85e4924452ae8225c8829f3fa8a2f7baa34bc5c (patch) | |
tree | 1c7fe6024c71a212c6c71d45ed695b842b8357eb /drivers/net/hyperv | |
parent | hv_netvsc: use skb_get_hash() instead of a homegrown implementation (diff) | |
download | linux-c85e4924452ae8225c8829f3fa8a2f7baa34bc5c.tar.xz linux-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.h | 1 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 33 |
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); } } |