diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml
index 81b58633911d..fb76410abd5b 100644
--- a/Documentation/netlink/specs/rt-link.yaml
+++ b/Documentation/netlink/specs/rt-link.yaml
@@ -1098,6 +1098,16 @@ attribute-sets:
name: pacing-offload
type: u32
doc: Enable EDT pacing offload (0 - disabled, 1 - enabled).
+ -
+ name: tx-extra-headroom
+ type: u32
+ doc: |
+ Additional headroom, in bytes, reserved on top of the driver's
+ requirement when the UDP, raw, AF_PACKET and AF_XDP copy mode transmit
+ paths allocate a packet for this device. TCP and forwarded packets are
+ not covered. Zero (the default) disables the extra reservation.
+ checks:
+ max: 512
-
name: prop-list-link-attrs
subset-of: link-attrs@@ -2471,6 +2481,7 @@ operations:
- gso-ipv4-max-size
- gro-ipv4-max-size
- af-spec
+ - tx-extra-headroom
-
name: newlink-ntf
doc: Notify that a link has been created@@ -2567,6 +2578,7 @@ operations:
- gso-ipv4-max-size
- gro-ipv4-max-size
- pacing-offload
+ - tx-extra-headroom
dump:
request:
value: 18diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst
index 6a87f09e7015..da3556e56cc2 100644
--- a/Documentation/networking/netdevices.rst
+++ b/Documentation/networking/netdevices.rst
@@ -441,6 +441,41 @@ from a notifier.
Drivers **must not** generate nested notifications of the ops-locked types.
+TX headroom reservation
+=======================
+
+``needed_headroom`` is the driver's own additional header-space requirement.
+It is written by the driver, or by a stacked device inheriting the value of
+the device below it, and is reported read-only through ``IFLA_HEADROOM``.
+
+``tx_extra_headroom`` is a separate reservation configured by the
+administrator through the ``IFLA_TX_EXTRA_HEADROOM`` rtnetlink attribute, a
+byte count from 0 to 512 that defaults to 0. It can be supplied when a link
+is created or changed, is reported in link dumps and change notifications,
+and setting it back to 0 removes it.
+
+The extra reservation is only applied when the stack allocates a locally
+generated packet for transmission through the device. ``LL_ALLOC_SPACE()``
+adds ``tx_extra_headroom`` before ``LL_RESERVED_SPACE()`` aligns the sum to
+``HH_DATA_MOD``, and is used by the UDP, raw and AF_PACKET transmit paths.
+AF_XDP copy mode adds it through ``netdev_tx_alloc_headroom()``. Its purpose
+is to give programs attached at TC egress room to push additional headers
+without having to reallocate the packet first.
+
+The setting is deliberately not part of ``LL_RESERVED_SPACE()``, which
+remains the threshold for deciding whether an already existing packet must
+be expanded. Packets that reach the device with less than the extra
+reservation, such as forwarded packets or TCP segments, are therefore not
+reallocated because of it. Consumers must keep their usual headroom and
+clone checks.
+
+The setting is not inherited through stacked devices. Packets are allocated
+against the device selected by the route, so configure it on the VLAN,
+bond or bridge device the route points at rather than on the device
+beneath it. It survives link down/up and network namespace moves and is
+independent of any TC or BPF attachment. It does not change the MTU or
+the bytes on the wire.
+
NETDEV_INTERNAL symbol namespace
================================
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 245b36204525..fb3a6b382a78 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -382,6 +382,7 @@ enum {
IFLA_HEADROOM,
IFLA_TAILROOM,
IFLA_PACING_OFFLOAD,
+ IFLA_TX_EXTRA_HEADROOM,
__IFLA_MAX
};
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index e141340172ce..16ee1a874107 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1409,6 +1409,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ nla_total_size(4) /* IFLA_PACING_OFFLOAD */
+ nla_total_size(2) /* IFLA_HEADROOM */
+ nla_total_size(2) /* IFLA_TAILROOM */
+ + nla_total_size(4) /* IFLA_TX_EXTRA_HEADROOM */
+ rtnl_dev_parent_size(dev)
+ 0;
@@ -2206,7 +2207,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
nla_put_u16(skb, IFLA_HEADROOM,
READ_ONCE(dev->needed_headroom)) ||
nla_put_u16(skb, IFLA_TAILROOM,
- READ_ONCE(dev->needed_tailroom)))
+ READ_ONCE(dev->needed_tailroom)) ||
+ nla_put_u32(skb, IFLA_TX_EXTRA_HEADROOM,
+ READ_ONCE(dev->tx_extra_headroom)))
goto nla_put_failure;
if (rtnl_fill_proto_down(skb, dev))
@@ -2367,6 +2370,8 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_HEADROOM] = { .type = NLA_REJECT },
[IFLA_TAILROOM] = { .type = NLA_REJECT },
[IFLA_PACING_OFFLOAD] = NLA_POLICY_MAX(NLA_U32, 1),
+ [IFLA_TX_EXTRA_HEADROOM] =
+ NLA_POLICY_MAX(NLA_U32, NETDEV_TX_EXTRA_HEADROOM_MAX),
};
static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {@@ -3374,6 +3379,15 @@ static int do_setlink(const struct sk_buff *skb, struct net_device *dev,
}
}
+ if (tb[IFLA_TX_EXTRA_HEADROOM]) {
+ u32 val = nla_get_u32(tb[IFLA_TX_EXTRA_HEADROOM]);
+
+ if (dev->tx_extra_headroom != val) {
+ WRITE_ONCE(dev->tx_extra_headroom, val);
+ status |= DO_SETLINK_NOTIFY;
+ }
+ }
+
if (tb[IFLA_OPERSTATE])
set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
@@ -3865,6 +3879,9 @@ struct net_device *rtnl_create_link(struct net *net, const char *ifname,
netif_set_gso_ipv4_max_size(dev, nla_get_u32(tb[IFLA_GSO_IPV4_MAX_SIZE]));
if (tb[IFLA_GRO_IPV4_MAX_SIZE])
netif_set_gro_ipv4_max_size(dev, nla_get_u32(tb[IFLA_GRO_IPV4_MAX_SIZE]));
+ if (tb[IFLA_TX_EXTRA_HEADROOM])
+ dev->tx_extra_headroom =
+ nla_get_u32(tb[IFLA_TX_EXTRA_HEADROOM]);
return dev;
}
--
2.56.0