hsr_create_tagged_frame() creates a new skb where the HSR header can be
prepended via __pskb_copy(). This does not copy the skb_shared_info part
of the original skb so any timestamp request is lost.
An alternative is to use skb_clone() where the skb_shared_info part is
shared and then skb_cow_head() to make the header part writeable. This
avoids touching manually the tx_flags and tskey members.
Using skb_clone() allows also return early (after the clone) in the
offloaded case so this can be unified a bit.
The clone case does not need manual adjustment of sk_buff::csum_start
because by using skb_cow_head() the head does not move relative to the
payload. Should the head need expansion then pskb_expand_head() will
move it and update sk_buff::csum_start accordingly. The clone also
preserves metadata (if available) so skb_postpush_data_move() is used
instead of memmove() to move both. Finally skb_reset_mac_len() is added
to set the new length of the header after expansion.
This is a preparation for PTP handling where timestamps are requested.
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
net/hsr/hsr_forward.c | 30 ++++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 44f6d9c83180a..b183358207506 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -331,7 +331,6 @@ static struct sk_buff *hsr_fill_tag(struct sk_buff *skb,
struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,
struct hsr_port *port)
{
- unsigned char *dst, *src;
struct sk_buff *skb;
int movelen;
@@ -342,33 +341,40 @@ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,
/* set the lane id properly */
hsr_set_path_id(frame, hsr_ethhdr, port);
return skb_clone(frame->skb_hsr, GFP_ATOMIC);
- } else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) {
- return skb_clone(frame->skb_std, GFP_ATOMIC);
}
- /* Create the new skb with enough headroom to fit the HSR tag */
- skb = __pskb_copy(frame->skb_std,
- skb_headroom(frame->skb_std) + HSR_HLEN, GFP_ATOMIC);
+ skb = skb_clone(frame->skb_std, GFP_ATOMIC);
if (!skb)
return NULL;
- skb_reset_mac_header(skb);
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- skb->csum_start += HSR_HLEN;
+ if (port->dev->features & NETIF_F_HW_HSR_TAG_INS)
+ return skb;
+
+ /* Ensure the cloned skb has enough headroom to fit the HSR tag */
+ if (skb_cow_head(skb, skb_metadata_len(skb) + HSR_HLEN))
+ goto err;
movelen = ETH_HLEN;
if (frame->is_vlan)
movelen += VLAN_HLEN;
- src = skb_mac_header(skb);
- dst = skb_push(skb, HSR_HLEN);
- memmove(dst, src, movelen);
+ /* The entire header area needs to be linear */
+ if (!pskb_may_pull(skb, movelen))
+ goto err;
+
+ skb_push(skb, HSR_HLEN);
+ skb_postpush_data_move(skb, HSR_HLEN, movelen);
+ skb_set_network_header(skb, HSR_HLEN + movelen);
skb_reset_mac_header(skb);
+ skb_reset_mac_len(skb);
/* skb_put_padto free skb on error and hsr_fill_tag returns NULL in
* that case
*/
return hsr_fill_tag(skb, frame, port, port->hsr->prot_version);
+err:
+ kfree_skb(skb);
+ return NULL;
}
struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,
--
2.55.0