2014-07-17 19:12:19

by Zoltan Kiss

[permalink] [raw]
Subject: [PATCH for stable 3.15 1/4] xen-netback: Fix handling frag_list on grant op error path

The error handling for skb's with frag_list was completely wrong, it caused
double unmap attempts to happen if the error was on the first skb. Move it to
the right place in the loop.

Signed-off-by: Zoltan Kiss <[email protected]>
Reported-by: Armin Zentai <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]

diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 7367208..d7ae93d 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -1007,10 +1007,16 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
{
struct gnttab_map_grant_ref *gop_map = *gopp_map;
u16 pending_idx = XENVIF_TX_CB(skb)->pending_idx;
+ /* This points to the shinfo of the actually checked skb, which could be
+ * either the first or the one on the frag_list
+ */
struct skb_shared_info *shinfo = skb_shinfo(skb);
+ /* If this is non-NULL, we are currently checking the frag_list skb, and
+ * this points to the shinfo of the first one
+ */
+ struct skb_shared_info *first_shinfo = NULL;
int nr_frags = shinfo->nr_frags;
int i, err;
- struct sk_buff *first_skb = NULL;

/* Check status of header. */
err = (*gopp_copy)->status;
@@ -1063,31 +1069,28 @@ check_frags:
xenvif_idx_unmap(vif, pending_idx);
}

+ /* And if we found the error while checking the frag_list, unmap
+ * the first skb's frags
+ */
+ if (first_shinfo) {
+ 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);
+ }
+ }
+
/* Remember the error: invalidate all subsequent fragments. */
err = newerr;
}

- if (skb_has_frag_list(skb)) {
- first_skb = skb;
- skb = shinfo->frag_list;
- shinfo = skb_shinfo(skb);
+ if (skb_has_frag_list(skb) && !first_shinfo) {
+ first_shinfo = skb_shinfo(skb);
+ shinfo = skb_shinfo(skb_shinfo(skb)->frag_list);
nr_frags = shinfo->nr_frags;

goto check_frags;
}

- /* There was a mapping error in the frag_list skb. We have to unmap
- * the first skb's frags
- */
- if (first_skb && err) {
- int j;
- shinfo = skb_shinfo(first_skb);
- for (j = 0; j < shinfo->nr_frags; j++) {
- pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
- xenvif_idx_unmap(vif, pending_idx);
- }
- }
-
*gopp_map = gop_map;
return err;
}


2014-07-17 19:12:22

by Zoltan Kiss

[permalink] [raw]
Subject: [PATCH for stable 3.15 3/4] xen-netback: Fix releasing header slot on error path

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
MAX_SKB_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 <[email protected]>
Reported-by: Armin Zentai <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 7b3e806..efc836e 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,26 @@ 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 +1103,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);
}
}

@@ -1807,8 +1836,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)

2014-07-17 19:12:55

by Zoltan Kiss

[permalink] [raw]
Subject: [PATCH for stable 3.15 4/4] xen-netback: Fix pointer incrementation to avoid incorrect logging

Due to this pointer is increased prematurely, the error log contains rubbish.

Signed-off-by: Zoltan Kiss <[email protected]>
Reported-by: Armin Zentai <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 7a36ecf..36fb4ff 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -1020,7 +1020,6 @@ static int xenvif_tx_check_gop(struct xenvif *vif,

/* Check status of header. */
err = (*gopp_copy)->status;
- (*gopp_copy)++;
if (unlikely(err)) {
if (net_ratelimit())
netdev_dbg(vif->dev,
@@ -1030,6 +1029,7 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
(*gopp_copy)->source.u.ref);
xenvif_idx_release(vif, first_pending_idx, XEN_NETIF_RSP_ERROR);
}
+ (*gopp_copy)++;

check_frags:
for (i = 0; i < nr_frags; i++, gop_map++) {

2014-07-17 19:13:24

by Zoltan Kiss

[permalink] [raw]
Subject: [PATCH for stable 3.15 2/4] xen-netback: Fix releasing frag_list skbs in error path

When the grant operations failed, the skb is freed up eventually, and it tries
to release the frags, if there is any. For the main skb nr_frags is set to 0 to
avoid this, but on the frag_list it iterates through the frags array, and tries
to call put_page on the page pointer which contains garbage at that time.

Signed-off-by: Zoltan Kiss <[email protected]>
Reported-by: Armin Zentai <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 1e19001..8ca6a26 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -1509,6 +1509,11 @@ static int xenvif_tx_submit(struct xenvif *vif)
/* Check the remap error code. */
if (unlikely(xenvif_tx_check_gop(vif, skb, &gop_map, &gop_copy))) {
skb_shinfo(skb)->nr_frags = 0;
+ if (skb_has_frag_list(skb)) {
+ struct sk_buff *nskb =
+ skb_shinfo(skb)->frag_list;
+ skb_shinfo(nskb)->nr_frags = 0;
+ }
kfree_skb(skb);
continue;
}