[Openvpn-devel,RFC,net-next,v2,1/9] ovpn: advertise checksum offload for GSO packets
Commit Message
ovpn already advertises software GSO support and segments GSO skbs in
its transmit path. However, without checksum offload in the device
features, the networking core has to segment GSO packets before they
reach ovpn because TCP GSO packets normally carry CHECKSUM_PARTIAL
state.
Advertise NETIF_F_HW_CSUM so the stack can pass such packets to ovpn.
Complete partial checksums after any GSO segmentation and before
submitting packets for encryption, since the inner packet checksum can
no longer be fixed after the packet has been encrypted.
Also pass the ovpn feature set to skb_gso_segment with GSO capabilities
masked out: this forces software segmentation, but still lets the
segmenter preserve supported non-GSO properties such as non-linear skb
data instead of needlessly linearizing.
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
---
No changes since v1 https://lore.kernel.org/openvpn-devel/1bbfde37488ade61928554db3a119525adf7b608.1789485693.git.ralf@mandelbit.com/
drivers/net/ovpn/io.c | 20 ++++++++++++++++++--
drivers/net/ovpn/main.c | 2 +-
2 files changed, 19 insertions(+), 3 deletions(-)
@@ -358,6 +358,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
struct ovpn_priv *ovpn = netdev_priv(dev);
struct sk_buff *segments, *curr, *next;
struct sk_buff_head skb_list;
+ netdev_features_t features;
unsigned int tx_bytes = 0;
struct ovpn_peer *peer;
__be16 proto;
@@ -392,8 +393,13 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
skb_dst_drop(skb);
if (skb_is_gso(skb)) {
- segments = skb_gso_segment(skb, 0);
- if (IS_ERR(segments)) {
+ /* force software segmentation, but keep ovpn's non-GSO feature
+ * bits so the generated segments can preserve non-linear skb
+ * data where possible
+ */
+ features = netif_skb_features(skb);
+ segments = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
+ if (IS_ERR_OR_NULL(segments)) {
ret = PTR_ERR(segments);
net_err_ratelimited("%s: cannot segment payload packet: %d\n",
netdev_name(dev), ret);
@@ -418,6 +424,16 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
continue;
}
+ /* NETIF_F_HW_CSUM requires completing partial checksums */
+ if (unlikely(curr->ip_summed == CHECKSUM_PARTIAL &&
+ skb_checksum_help(curr) < 0)) {
+ net_err_ratelimited("%s: skb_checksum_help failed for payload packet\n",
+ netdev_name(dev));
+ ovpn_dev_dstats_tx_dropped(ovpn->dev);
+ kfree_skb(curr);
+ continue;
+ }
+
/* only count what we actually send */
tx_bytes += curr->len;
__skb_queue_tail(&skb_list, curr);
@@ -157,7 +157,7 @@ static const struct ethtool_ops ovpn_ethtool_ops = {
static void ovpn_setup(struct net_device *dev)
{
- netdev_features_t feat = NETIF_F_SG | NETIF_F_GSO |
+ netdev_features_t feat = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_GSO |
NETIF_F_GSO_SOFTWARE | NETIF_F_HIGHDMA;
dev->needs_free_netdev = true;