Allocate the dst route dynamically rather than on stack, reducing ovs_fragment stack usage from 400 to 160 bytes, at a cost of a GFP_ATOMIC allocation.
Signed-off-by: Nicholas Piggin <npig...@gmail.com> --- net/openvswitch/actions.c | 33 ++++++++++++++++++++++++--------- net/openvswitch/drop.h | 1 + 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 12ad998b70e2..a6e10f59838f 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -868,38 +868,53 @@ void ovs_fragment(struct net *net, struct vport *vport, struct sk_buff *skb, } if (key->eth.type == htons(ETH_P_IP)) { - struct rtable ovs_rt = { 0 }; + struct rtable *ovs_rt; unsigned long orig_dst; + ovs_rt = kzalloc(sizeof(*ovs_rt), GFP_ATOMIC); + if (!ovs_rt) { + OVS_NLERR(1, "No memory to fragment"); + reason = OVS_DROP_NOMEM; + goto err; + } + prepare_frag(vport, skb, orig_network_offset, ovs_key_mac_proto(key)); - dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, 1, + dst_init(&ovs_rt->dst, &ovs_dst_ops, NULL, 1, DST_OBSOLETE_NONE, DST_NOCOUNT); - ovs_rt.dst.dev = vport->dev; + ovs_rt->dst.dev = vport->dev; orig_dst = skb->_skb_refdst; - skb_dst_set_noref(skb, &ovs_rt.dst); + skb_dst_set_noref(skb, &ovs_rt->dst); IPCB(skb)->frag_max_size = mru; ip_do_fragment(net, skb->sk, skb, ovs_vport_output); refdst_drop(orig_dst); + kfree(ovs_rt); } else if (key->eth.type == htons(ETH_P_IPV6)) { unsigned long orig_dst; - struct rt6_info ovs_rt; + struct rt6_info *ovs_rt; + + ovs_rt = kzalloc(sizeof(*ovs_rt), GFP_ATOMIC); + if (!ovs_rt) { + OVS_NLERR(1, "No memory to fragment"); + reason = OVS_DROP_NOMEM; + goto err; + } prepare_frag(vport, skb, orig_network_offset, ovs_key_mac_proto(key)); - memset(&ovs_rt, 0, sizeof(ovs_rt)); - dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, 1, + dst_init(&ovs_rt->dst, &ovs_dst_ops, NULL, 1, DST_OBSOLETE_NONE, DST_NOCOUNT); - ovs_rt.dst.dev = vport->dev; + ovs_rt->dst.dev = vport->dev; orig_dst = skb->_skb_refdst; - skb_dst_set_noref(skb, &ovs_rt.dst); + skb_dst_set_noref(skb, &ovs_rt->dst); IP6CB(skb)->frag_max_size = mru; ipv6_stub->ipv6_fragment(net, skb->sk, skb, ovs_vport_output); refdst_drop(orig_dst); + kfree(ovs_rt); } else { WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.", ovs_vport_name(vport), ntohs(key->eth.type), mru, diff --git a/net/openvswitch/drop.h b/net/openvswitch/drop.h index cedf9b7b5796..0bf156867a69 100644 --- a/net/openvswitch/drop.h +++ b/net/openvswitch/drop.h @@ -20,6 +20,7 @@ R(OVS_DROP_FRAG_INVALID_PROTO) \ R(OVS_DROP_CONNTRACK) \ R(OVS_DROP_IP_TTL) \ + R(OVS_DROP_NOMEM) \ /* deliberate comment for trailing \ */ enum ovs_drop_reason { -- 2.40.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev