diff options
author | Zoltan Kiss <zoltan.kiss@citrix.com> | 2014-07-18 19:08:04 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-07-28 08:08:26 -0700 |
commit | 4500fc0ec693850ce8cd6ae505cbffcc73f91786 (patch) | |
tree | a02a99f16395d4809aeb793ad5481aa37d0307d9 /drivers | |
parent | 50b771050668d2df87f9f5488d860e9696a0a5fc (diff) |
xen-netback: Fix releasing header slot on error path
[ Upstream commit 1b860da0404a76af8533099ffe0a965490939369 ]
This patch makes this function aware that the first frag and the header might
share the same ring slot. That could happen if the first slot is bigger than
PKT_PROT_LEN. Due to this the error path might release that slot twice or never,
depending on the error scenario.
xenvif_idx_release is also removed from xenvif_idx_unmap, and called separately.
Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Reported-by: Armin Zentai <armin.zentai@ezit.hu>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/xen-netback/netback.c | 38 |
1 files changed, 33 insertions, 5 deletions
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 75b7d1a1ada..2c5a6f9f61d 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1016,6 +1016,8 @@ static int xenvif_tx_check_gop(struct xenvif *vif, */ struct skb_shared_info *first_shinfo = NULL; int nr_frags = shinfo->nr_frags; + const bool sharedslot = nr_frags && + frag_get_pending_idx(&shinfo->frags[0]) == pending_idx; int i, err; /* Check status of header. */ @@ -1028,7 +1030,10 @@ static int xenvif_tx_check_gop(struct xenvif *vif, (*gopp_copy)->status, pending_idx, (*gopp_copy)->source.u.ref); - xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR); + /* The first frag might still have this slot mapped */ + if (!sharedslot) + xenvif_idx_release(vif, pending_idx, + XEN_NETIF_RSP_ERROR); } check_frags: @@ -1045,8 +1050,19 @@ check_frags: pending_idx, gop_map->handle); /* Had a previous error? Invalidate this fragment. */ - if (unlikely(err)) + if (unlikely(err)) { xenvif_idx_unmap(vif, pending_idx); + /* If the mapping of the first frag was OK, but + * the header's copy failed, and they are + * sharing a slot, send an error + */ + if (i == 0 && sharedslot) + xenvif_idx_release(vif, pending_idx, + XEN_NETIF_RSP_ERROR); + else + xenvif_idx_release(vif, pending_idx, + XEN_NETIF_RSP_OKAY); + } continue; } @@ -1058,15 +1074,27 @@ check_frags: gop_map->status, pending_idx, gop_map->ref); + xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR); /* Not the first error? Preceding frags already invalidated. */ if (err) continue; - /* First error: invalidate preceding fragments. */ + + /* First error: if the header haven't shared a slot with the + * first frag, release it as well. + */ + if (!sharedslot) + xenvif_idx_release(vif, + XENVIF_TX_CB(skb)->pending_idx, + XEN_NETIF_RSP_OKAY); + + /* Invalidate preceding fragments of this skb. */ for (j = 0; j < i; j++) { pending_idx = frag_get_pending_idx(&shinfo->frags[j]); xenvif_idx_unmap(vif, pending_idx); + xenvif_idx_release(vif, pending_idx, + XEN_NETIF_RSP_OKAY); } /* And if we found the error while checking the frag_list, unmap @@ -1076,6 +1104,8 @@ check_frags: for (j = 0; j < first_shinfo->nr_frags; j++) { pending_idx = frag_get_pending_idx(&first_shinfo->frags[j]); xenvif_idx_unmap(vif, pending_idx); + xenvif_idx_release(vif, pending_idx, + XEN_NETIF_RSP_OKAY); } } @@ -1811,8 +1841,6 @@ void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx) tx_unmap_op.status); BUG(); } - - xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY); } static inline int rx_work_todo(struct xenvif *vif) |