mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'for-netdev' of https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Martin KaFai Lau says: ==================== pull-request: bpf-next 2025-02-20 We've added 19 non-merge commits during the last 8 day(s) which contain a total of 35 files changed, 1126 insertions(+), 53 deletions(-). The main changes are: 1) Add TCP_RTO_MAX_MS support to bpf_set/getsockopt, from Jason Xing 2) Add network TX timestamping support to BPF sock_ops, from Jason Xing 3) Add TX metadata Launch Time support, from Song Yoong Siang * tag 'for-netdev' of https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: igc: Add launch time support to XDP ZC igc: Refactor empty frame insertion for launch time support net: stmmac: Add launch time support to XDP ZC selftests/bpf: Add launch time request to xdp_hw_metadata xsk: Add launch time hardware offload support to XDP Tx metadata selftests/bpf: Add simple bpf tests in the tx path for timestamping feature bpf: Support selective sampling for bpf timestamping bpf: Add BPF_SOCK_OPS_TSTAMP_SENDMSG_CB callback bpf: Add BPF_SOCK_OPS_TSTAMP_ACK_CB callback bpf: Add BPF_SOCK_OPS_TSTAMP_SND_HW_CB callback bpf: Add BPF_SOCK_OPS_TSTAMP_SND_SW_CB callback bpf: Add BPF_SOCK_OPS_TSTAMP_SCHED_CB callback net-timestamp: Prepare for isolating two modes of SO_TIMESTAMPING bpf: Disable unsafe helpers in TX timestamping callbacks bpf: Prevent unsafe access to the sock fields in the BPF timestamping callback bpf: Prepare the sock_ops ctx and call bpf prog for TX timestamping bpf: Add networking timestamping support to bpf_get/setsockopt() selftests/bpf: Add rto max for bpf_setsockopt test bpf: Support TCP_RTO_MAX_MS for bpf_setsockopt ==================== Link: https://patch.msgid.link/20250221022104.386462-1-martin.lau@linux.dev Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -70,6 +70,10 @@ definitions:
|
||||
name: tx-checksum
|
||||
doc:
|
||||
L3 checksum HW offload is supported by the driver.
|
||||
-
|
||||
name: tx-launch-time-fifo
|
||||
doc:
|
||||
Launch time HW offload is supported by the driver.
|
||||
-
|
||||
name: queue-type
|
||||
type: enum
|
||||
|
||||
@@ -50,6 +50,10 @@ The flags field enables the particular offload:
|
||||
checksum. ``csum_start`` specifies byte offset of where the checksumming
|
||||
should start and ``csum_offset`` specifies byte offset where the
|
||||
device should store the computed checksum.
|
||||
- ``XDP_TXMD_FLAGS_LAUNCH_TIME``: requests the device to schedule the
|
||||
packet for transmission at a pre-determined time called launch time. The
|
||||
value of launch time is indicated by ``launch_time`` field of
|
||||
``union xsk_tx_metadata``.
|
||||
|
||||
Besides the flags above, in order to trigger the offloads, the first
|
||||
packet's ``struct xdp_desc`` descriptor should set ``XDP_TX_METADATA``
|
||||
@@ -65,6 +69,63 @@ In this case, when running in ``XDK_COPY`` mode, the TX checksum
|
||||
is calculated on the CPU. Do not enable this option in production because
|
||||
it will negatively affect performance.
|
||||
|
||||
Launch Time
|
||||
===========
|
||||
|
||||
The value of the requested launch time should be based on the device's PTP
|
||||
Hardware Clock (PHC) to ensure accuracy. AF_XDP takes a different data path
|
||||
compared to the ETF queuing discipline, which organizes packets and delays
|
||||
their transmission. Instead, AF_XDP immediately hands off the packets to
|
||||
the device driver without rearranging their order or holding them prior to
|
||||
transmission. Since the driver maintains FIFO behavior and does not perform
|
||||
packet reordering, a packet with a launch time request will block other
|
||||
packets in the same Tx Queue until it is sent. Therefore, it is recommended
|
||||
to allocate separate queue for scheduling traffic that is intended for
|
||||
future transmission.
|
||||
|
||||
In scenarios where the launch time offload feature is disabled, the device
|
||||
driver is expected to disregard the launch time request. For correct
|
||||
interpretation and meaningful operation, the launch time should never be
|
||||
set to a value larger than the farthest programmable time in the future
|
||||
(the horizon). Different devices have different hardware limitations on the
|
||||
launch time offload feature.
|
||||
|
||||
stmmac driver
|
||||
-------------
|
||||
|
||||
For stmmac, TSO and launch time (TBS) features are mutually exclusive for
|
||||
each individual Tx Queue. By default, the driver configures Tx Queue 0 to
|
||||
support TSO and the rest of the Tx Queues to support TBS. The launch time
|
||||
hardware offload feature can be enabled or disabled by using the tc-etf
|
||||
command to call the driver's ndo_setup_tc() callback.
|
||||
|
||||
The value of the launch time that is programmed in the Enhanced Normal
|
||||
Transmit Descriptors is a 32-bit value, where the most significant 8 bits
|
||||
represent the time in seconds and the remaining 24 bits represent the time
|
||||
in 256 ns increments. The programmed launch time is compared against the
|
||||
PTP time (bits[39:8]) and rolls over after 256 seconds. Therefore, the
|
||||
horizon of the launch time for dwmac4 and dwxlgmac2 is 128 seconds in the
|
||||
future.
|
||||
|
||||
igc driver
|
||||
----------
|
||||
|
||||
For igc, all four Tx Queues support the launch time feature. The launch
|
||||
time hardware offload feature can be enabled or disabled by using the
|
||||
tc-etf command to call the driver's ndo_setup_tc() callback. When entering
|
||||
TSN mode, the igc driver will reset the device and create a default Qbv
|
||||
schedule with a 1-second cycle time, with all Tx Queues open at all times.
|
||||
|
||||
The value of the launch time that is programmed in the Advanced Transmit
|
||||
Context Descriptor is a relative offset to the starting time of the Qbv
|
||||
transmission window of the queue. The Frst flag of the descriptor can be
|
||||
set to schedule the packet for the next Qbv cycle. Therefore, the horizon
|
||||
of the launch time for i225 and i226 is the ending time of the next cycle
|
||||
of the Qbv transmission window of the queue. For example, when the Qbv
|
||||
cycle time is set to 1 second, the horizon of the launch time ranges
|
||||
from 1 second to 2 seconds, depending on where the Qbv cycle is currently
|
||||
running.
|
||||
|
||||
Querying Device Capabilities
|
||||
============================
|
||||
|
||||
@@ -74,6 +135,7 @@ Refer to ``xsk-flags`` features bitmask in
|
||||
|
||||
- ``tx-timestamp``: device supports ``XDP_TXMD_FLAGS_TIMESTAMP``
|
||||
- ``tx-checksum``: device supports ``XDP_TXMD_FLAGS_CHECKSUM``
|
||||
- ``tx-launch-time-fifo``: device supports ``XDP_TXMD_FLAGS_LAUNCH_TIME``
|
||||
|
||||
See ``tools/net/ynl/samples/netdev.c`` on how to query this information.
|
||||
|
||||
|
||||
@@ -579,6 +579,7 @@ struct igc_metadata_request {
|
||||
struct xsk_tx_metadata *meta;
|
||||
struct igc_ring *tx_ring;
|
||||
u32 cmd_type;
|
||||
u16 used_desc;
|
||||
};
|
||||
|
||||
struct igc_q_vector {
|
||||
|
||||
@@ -1092,7 +1092,8 @@ static int igc_init_empty_frame(struct igc_ring *ring,
|
||||
|
||||
dma = dma_map_single(ring->dev, skb->data, size, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(ring->dev, dma)) {
|
||||
netdev_err_once(ring->netdev, "Failed to map DMA for TX\n");
|
||||
net_err_ratelimited("%s: DMA mapping error for empty frame\n",
|
||||
netdev_name(ring->netdev));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -1108,20 +1109,12 @@ static int igc_init_empty_frame(struct igc_ring *ring,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int igc_init_tx_empty_descriptor(struct igc_ring *ring,
|
||||
struct sk_buff *skb,
|
||||
struct igc_tx_buffer *first)
|
||||
static void igc_init_tx_empty_descriptor(struct igc_ring *ring,
|
||||
struct sk_buff *skb,
|
||||
struct igc_tx_buffer *first)
|
||||
{
|
||||
union igc_adv_tx_desc *desc;
|
||||
u32 cmd_type, olinfo_status;
|
||||
int err;
|
||||
|
||||
if (!igc_desc_unused(ring))
|
||||
return -EBUSY;
|
||||
|
||||
err = igc_init_empty_frame(ring, first, skb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cmd_type = IGC_ADVTXD_DTYP_DATA | IGC_ADVTXD_DCMD_DEXT |
|
||||
IGC_ADVTXD_DCMD_IFCS | IGC_TXD_DCMD |
|
||||
@@ -1140,8 +1133,6 @@ static int igc_init_tx_empty_descriptor(struct igc_ring *ring,
|
||||
ring->next_to_use++;
|
||||
if (ring->next_to_use == ring->count)
|
||||
ring->next_to_use = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IGC_EMPTY_FRAME_SIZE 60
|
||||
@@ -1567,6 +1558,40 @@ static bool igc_request_tx_tstamp(struct igc_adapter *adapter, struct sk_buff *s
|
||||
return false;
|
||||
}
|
||||
|
||||
static int igc_insert_empty_frame(struct igc_ring *tx_ring)
|
||||
{
|
||||
struct igc_tx_buffer *empty_info;
|
||||
struct sk_buff *empty_skb;
|
||||
void *data;
|
||||
int ret;
|
||||
|
||||
empty_info = &tx_ring->tx_buffer_info[tx_ring->next_to_use];
|
||||
empty_skb = alloc_skb(IGC_EMPTY_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (unlikely(!empty_skb)) {
|
||||
net_err_ratelimited("%s: skb alloc error for empty frame\n",
|
||||
netdev_name(tx_ring->netdev));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = skb_put(empty_skb, IGC_EMPTY_FRAME_SIZE);
|
||||
memset(data, 0, IGC_EMPTY_FRAME_SIZE);
|
||||
|
||||
/* Prepare DMA mapping and Tx buffer information */
|
||||
ret = igc_init_empty_frame(tx_ring, empty_info, empty_skb);
|
||||
if (unlikely(ret)) {
|
||||
dev_kfree_skb_any(empty_skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Prepare advanced context descriptor for empty packet */
|
||||
igc_tx_ctxtdesc(tx_ring, 0, false, 0, 0, 0);
|
||||
|
||||
/* Prepare advanced data descriptor for empty packet */
|
||||
igc_init_tx_empty_descriptor(tx_ring, empty_skb, empty_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,
|
||||
struct igc_ring *tx_ring)
|
||||
{
|
||||
@@ -1586,6 +1611,7 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,
|
||||
* + 1 desc for skb_headlen/IGC_MAX_DATA_PER_TXD,
|
||||
* + 2 desc gap to keep tail from touching head,
|
||||
* + 1 desc for context descriptor,
|
||||
* + 2 desc for inserting an empty packet for launch time,
|
||||
* otherwise try next time
|
||||
*/
|
||||
for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
|
||||
@@ -1605,24 +1631,16 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,
|
||||
launch_time = igc_tx_launchtime(tx_ring, txtime, &first_flag, &insert_empty);
|
||||
|
||||
if (insert_empty) {
|
||||
struct igc_tx_buffer *empty_info;
|
||||
struct sk_buff *empty;
|
||||
void *data;
|
||||
|
||||
empty_info = &tx_ring->tx_buffer_info[tx_ring->next_to_use];
|
||||
empty = alloc_skb(IGC_EMPTY_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!empty)
|
||||
goto done;
|
||||
|
||||
data = skb_put(empty, IGC_EMPTY_FRAME_SIZE);
|
||||
memset(data, 0, IGC_EMPTY_FRAME_SIZE);
|
||||
|
||||
igc_tx_ctxtdesc(tx_ring, 0, false, 0, 0, 0);
|
||||
|
||||
if (igc_init_tx_empty_descriptor(tx_ring,
|
||||
empty,
|
||||
empty_info) < 0)
|
||||
dev_kfree_skb_any(empty);
|
||||
/* Reset the launch time if the required empty frame fails to
|
||||
* be inserted. However, this packet is not dropped, so it
|
||||
* "dirties" the current Qbv cycle. This ensures that the
|
||||
* upcoming packet, which is scheduled in the next Qbv cycle,
|
||||
* does not require an empty frame. This way, the launch time
|
||||
* continues to function correctly despite the current failure
|
||||
* to insert the empty frame.
|
||||
*/
|
||||
if (igc_insert_empty_frame(tx_ring))
|
||||
launch_time = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
@@ -2953,9 +2971,48 @@ static u64 igc_xsk_fill_timestamp(void *_priv)
|
||||
return *(u64 *)_priv;
|
||||
}
|
||||
|
||||
static void igc_xsk_request_launch_time(u64 launch_time, void *_priv)
|
||||
{
|
||||
struct igc_metadata_request *meta_req = _priv;
|
||||
struct igc_ring *tx_ring = meta_req->tx_ring;
|
||||
__le32 launch_time_offset;
|
||||
bool insert_empty = false;
|
||||
bool first_flag = false;
|
||||
u16 used_desc = 0;
|
||||
|
||||
if (!tx_ring->launchtime_enable)
|
||||
return;
|
||||
|
||||
launch_time_offset = igc_tx_launchtime(tx_ring,
|
||||
ns_to_ktime(launch_time),
|
||||
&first_flag, &insert_empty);
|
||||
if (insert_empty) {
|
||||
/* Disregard the launch time request if the required empty frame
|
||||
* fails to be inserted.
|
||||
*/
|
||||
if (igc_insert_empty_frame(tx_ring))
|
||||
return;
|
||||
|
||||
meta_req->tx_buffer =
|
||||
&tx_ring->tx_buffer_info[tx_ring->next_to_use];
|
||||
/* Inserting an empty packet requires two descriptors:
|
||||
* one data descriptor and one context descriptor.
|
||||
*/
|
||||
used_desc += 2;
|
||||
}
|
||||
|
||||
/* Use one context descriptor to specify launch time and first flag. */
|
||||
igc_tx_ctxtdesc(tx_ring, launch_time_offset, first_flag, 0, 0, 0);
|
||||
used_desc += 1;
|
||||
|
||||
/* Update the number of used descriptors in this request */
|
||||
meta_req->used_desc += used_desc;
|
||||
}
|
||||
|
||||
const struct xsk_tx_metadata_ops igc_xsk_tx_metadata_ops = {
|
||||
.tmo_request_timestamp = igc_xsk_request_timestamp,
|
||||
.tmo_fill_timestamp = igc_xsk_fill_timestamp,
|
||||
.tmo_request_launch_time = igc_xsk_request_launch_time,
|
||||
};
|
||||
|
||||
static void igc_xdp_xmit_zc(struct igc_ring *ring)
|
||||
@@ -2978,7 +3035,13 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)
|
||||
ntu = ring->next_to_use;
|
||||
budget = igc_desc_unused(ring);
|
||||
|
||||
while (xsk_tx_peek_desc(pool, &xdp_desc) && budget--) {
|
||||
/* Packets with launch time require one data descriptor and one context
|
||||
* descriptor. When the launch time falls into the next Qbv cycle, we
|
||||
* may need to insert an empty packet, which requires two more
|
||||
* descriptors. Therefore, to be safe, we always ensure we have at least
|
||||
* 4 descriptors available.
|
||||
*/
|
||||
while (xsk_tx_peek_desc(pool, &xdp_desc) && budget >= 4) {
|
||||
struct igc_metadata_request meta_req;
|
||||
struct xsk_tx_metadata *meta = NULL;
|
||||
struct igc_tx_buffer *bi;
|
||||
@@ -2999,9 +3062,19 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)
|
||||
meta_req.tx_ring = ring;
|
||||
meta_req.tx_buffer = bi;
|
||||
meta_req.meta = meta;
|
||||
meta_req.used_desc = 0;
|
||||
xsk_tx_metadata_request(meta, &igc_xsk_tx_metadata_ops,
|
||||
&meta_req);
|
||||
|
||||
/* xsk_tx_metadata_request() may have updated next_to_use */
|
||||
ntu = ring->next_to_use;
|
||||
|
||||
/* xsk_tx_metadata_request() may have updated Tx buffer info */
|
||||
bi = meta_req.tx_buffer;
|
||||
|
||||
/* xsk_tx_metadata_request() may use a few descriptors */
|
||||
budget -= meta_req.used_desc;
|
||||
|
||||
tx_desc = IGC_TX_DESC(ring, ntu);
|
||||
tx_desc->read.cmd_type_len = cpu_to_le32(meta_req.cmd_type);
|
||||
tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
|
||||
@@ -3019,9 +3092,11 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)
|
||||
ntu++;
|
||||
if (ntu == ring->count)
|
||||
ntu = 0;
|
||||
|
||||
ring->next_to_use = ntu;
|
||||
budget--;
|
||||
}
|
||||
|
||||
ring->next_to_use = ntu;
|
||||
if (tx_desc) {
|
||||
igc_flush_tx_descriptors(ring);
|
||||
xsk_tx_release(pool);
|
||||
|
||||
@@ -106,6 +106,8 @@ struct stmmac_metadata_request {
|
||||
struct stmmac_priv *priv;
|
||||
struct dma_desc *tx_desc;
|
||||
bool *set_ic;
|
||||
struct dma_edesc *edesc;
|
||||
int tbs;
|
||||
};
|
||||
|
||||
struct stmmac_xsk_tx_complete {
|
||||
|
||||
@@ -2486,9 +2486,20 @@ static u64 stmmac_xsk_fill_timestamp(void *_priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stmmac_xsk_request_launch_time(u64 launch_time, void *_priv)
|
||||
{
|
||||
struct timespec64 ts = ns_to_timespec64(launch_time);
|
||||
struct stmmac_metadata_request *meta_req = _priv;
|
||||
|
||||
if (meta_req->tbs & STMMAC_TBS_EN)
|
||||
stmmac_set_desc_tbs(meta_req->priv, meta_req->edesc, ts.tv_sec,
|
||||
ts.tv_nsec);
|
||||
}
|
||||
|
||||
static const struct xsk_tx_metadata_ops stmmac_xsk_tx_metadata_ops = {
|
||||
.tmo_request_timestamp = stmmac_xsk_request_timestamp,
|
||||
.tmo_fill_timestamp = stmmac_xsk_fill_timestamp,
|
||||
.tmo_request_launch_time = stmmac_xsk_request_launch_time,
|
||||
};
|
||||
|
||||
static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
|
||||
@@ -2572,6 +2583,8 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
|
||||
meta_req.priv = priv;
|
||||
meta_req.tx_desc = tx_desc;
|
||||
meta_req.set_ic = &set_ic;
|
||||
meta_req.tbs = tx_q->tbs;
|
||||
meta_req.edesc = &tx_q->dma_entx[entry];
|
||||
xsk_tx_metadata_request(meta, &stmmac_xsk_tx_metadata_ops,
|
||||
&meta_req);
|
||||
if (set_ic) {
|
||||
|
||||
@@ -1508,6 +1508,7 @@ struct bpf_sock_ops_kern {
|
||||
void *skb_data_end;
|
||||
u8 op;
|
||||
u8 is_fullsock;
|
||||
u8 is_locked_tcp_sock;
|
||||
u8 remaining_opt_len;
|
||||
u64 temp; /* temp and everything after is not
|
||||
* initialized to 0 before calling
|
||||
|
||||
@@ -470,7 +470,7 @@ struct skb_shared_hwtstamps {
|
||||
/* Definitions for tx_flags in struct skb_shared_info */
|
||||
enum {
|
||||
/* generate hardware time stamp */
|
||||
SKBTX_HW_TSTAMP = 1 << 0,
|
||||
SKBTX_HW_TSTAMP_NOBPF = 1 << 0,
|
||||
|
||||
/* generate software time stamp when queueing packet to NIC */
|
||||
SKBTX_SW_TSTAMP = 1 << 1,
|
||||
@@ -489,10 +489,16 @@ enum {
|
||||
|
||||
/* generate software time stamp when entering packet scheduling */
|
||||
SKBTX_SCHED_TSTAMP = 1 << 6,
|
||||
|
||||
/* used for bpf extension when a bpf program is loaded */
|
||||
SKBTX_BPF = 1 << 7,
|
||||
};
|
||||
|
||||
#define SKBTX_HW_TSTAMP (SKBTX_HW_TSTAMP_NOBPF | SKBTX_BPF)
|
||||
|
||||
#define SKBTX_ANY_SW_TSTAMP (SKBTX_SW_TSTAMP | \
|
||||
SKBTX_SCHED_TSTAMP)
|
||||
SKBTX_SCHED_TSTAMP | \
|
||||
SKBTX_BPF)
|
||||
#define SKBTX_ANY_TSTAMP (SKBTX_HW_TSTAMP | \
|
||||
SKBTX_HW_TSTAMP_USE_CYCLES | \
|
||||
SKBTX_ANY_SW_TSTAMP)
|
||||
@@ -4564,7 +4570,7 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
|
||||
static inline void skb_tx_timestamp(struct sk_buff *skb)
|
||||
{
|
||||
skb_clone_tx_timestamp(skb);
|
||||
if (skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP)
|
||||
if (skb_shinfo(skb)->tx_flags & (SKBTX_SW_TSTAMP | SKBTX_BPF))
|
||||
skb_tstamp_tx(skb, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +303,7 @@ struct sk_filter;
|
||||
* @sk_stamp: time stamp of last packet received
|
||||
* @sk_stamp_seq: lock for accessing sk_stamp on 32 bit architectures only
|
||||
* @sk_tsflags: SO_TIMESTAMPING flags
|
||||
* @sk_bpf_cb_flags: used in bpf_setsockopt()
|
||||
* @sk_use_task_frag: allow sk_page_frag() to use current->task_frag.
|
||||
* Sockets that can be used under memory reclaim should
|
||||
* set this to false.
|
||||
@@ -525,6 +526,8 @@ struct sock {
|
||||
u8 sk_txtime_deadline_mode : 1,
|
||||
sk_txtime_report_errors : 1,
|
||||
sk_txtime_unused : 6;
|
||||
#define SK_BPF_CB_FLAG_TEST(SK, FLAG) ((SK)->sk_bpf_cb_flags & (FLAG))
|
||||
u8 sk_bpf_cb_flags;
|
||||
|
||||
void *sk_user_data;
|
||||
#ifdef CONFIG_SECURITY
|
||||
@@ -2909,6 +2912,13 @@ int sock_set_timestamping(struct sock *sk, int optname,
|
||||
struct so_timestamping timestamping);
|
||||
|
||||
void sock_enable_timestamps(struct sock *sk);
|
||||
#if defined(CONFIG_CGROUP_BPF)
|
||||
void bpf_skops_tx_timestamping(struct sock *sk, struct sk_buff *skb, int op);
|
||||
#else
|
||||
static inline void bpf_skops_tx_timestamping(struct sock *sk, struct sk_buff *skb, int op)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
void sock_no_linger(struct sock *sk);
|
||||
void sock_set_keepalive(struct sock *sk);
|
||||
void sock_set_priority(struct sock *sk, u32 priority);
|
||||
|
||||
+5
-2
@@ -978,10 +978,12 @@ struct tcp_skb_cb {
|
||||
|
||||
__u8 sacked; /* State flags for SACK. */
|
||||
__u8 ip_dsfield; /* IPv4 tos or IPv6 dsfield */
|
||||
__u8 txstamp_ack:1, /* Record TX timestamp for ack? */
|
||||
#define TSTAMP_ACK_SK 0x1
|
||||
#define TSTAMP_ACK_BPF 0x2
|
||||
__u8 txstamp_ack:2, /* Record TX timestamp for ack? */
|
||||
eor:1, /* Is skb MSG_EOR marked? */
|
||||
has_rxtstamp:1, /* SKB has a RX timestamp */
|
||||
unused:5;
|
||||
unused:4;
|
||||
__u32 ack_seq; /* Sequence number ACK'd */
|
||||
union {
|
||||
struct {
|
||||
@@ -2671,6 +2673,7 @@ static inline int tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args)
|
||||
memset(&sock_ops, 0, offsetof(struct bpf_sock_ops_kern, temp));
|
||||
if (sk_fullsock(sk)) {
|
||||
sock_ops.is_fullsock = 1;
|
||||
sock_ops.is_locked_tcp_sock = 1;
|
||||
sock_owned_by_me(sk);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,11 +110,16 @@ struct xdp_sock {
|
||||
* indicates position where checksumming should start.
|
||||
* csum_offset indicates position where checksum should be stored.
|
||||
*
|
||||
* void (*tmo_request_launch_time)(u64 launch_time, void *priv)
|
||||
* Called when AF_XDP frame requested launch time HW offload support.
|
||||
* launch_time indicates the PTP time at which the device can schedule the
|
||||
* packet for transmission.
|
||||
*/
|
||||
struct xsk_tx_metadata_ops {
|
||||
void (*tmo_request_timestamp)(void *priv);
|
||||
u64 (*tmo_fill_timestamp)(void *priv);
|
||||
void (*tmo_request_checksum)(u16 csum_start, u16 csum_offset, void *priv);
|
||||
void (*tmo_request_launch_time)(u64 launch_time, void *priv);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_XDP_SOCKETS
|
||||
@@ -162,6 +167,11 @@ static inline void xsk_tx_metadata_request(const struct xsk_tx_metadata *meta,
|
||||
if (!meta)
|
||||
return;
|
||||
|
||||
if (ops->tmo_request_launch_time)
|
||||
if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
|
||||
ops->tmo_request_launch_time(meta->request.launch_time,
|
||||
priv);
|
||||
|
||||
if (ops->tmo_request_timestamp)
|
||||
if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)
|
||||
ops->tmo_request_timestamp(priv);
|
||||
|
||||
@@ -216,6 +216,7 @@ xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr)
|
||||
#define XDP_TXMD_FLAGS_VALID ( \
|
||||
XDP_TXMD_FLAGS_TIMESTAMP | \
|
||||
XDP_TXMD_FLAGS_CHECKSUM | \
|
||||
XDP_TXMD_FLAGS_LAUNCH_TIME | \
|
||||
0)
|
||||
|
||||
static inline bool
|
||||
|
||||
@@ -6913,6 +6913,12 @@ enum {
|
||||
BPF_SOCK_OPS_ALL_CB_FLAGS = 0x7F,
|
||||
};
|
||||
|
||||
enum {
|
||||
SK_BPF_CB_TX_TIMESTAMPING = 1<<0,
|
||||
SK_BPF_CB_MASK = (SK_BPF_CB_TX_TIMESTAMPING - 1) |
|
||||
SK_BPF_CB_TX_TIMESTAMPING
|
||||
};
|
||||
|
||||
/* List of known BPF sock_ops operators.
|
||||
* New entries can only be added at the end
|
||||
*/
|
||||
@@ -7025,6 +7031,29 @@ enum {
|
||||
* by the kernel or the
|
||||
* earlier bpf-progs.
|
||||
*/
|
||||
BPF_SOCK_OPS_TSTAMP_SCHED_CB, /* Called when skb is passing
|
||||
* through dev layer when
|
||||
* SK_BPF_CB_TX_TIMESTAMPING
|
||||
* feature is on.
|
||||
*/
|
||||
BPF_SOCK_OPS_TSTAMP_SND_SW_CB, /* Called when skb is about to send
|
||||
* to the nic when SK_BPF_CB_TX_TIMESTAMPING
|
||||
* feature is on.
|
||||
*/
|
||||
BPF_SOCK_OPS_TSTAMP_SND_HW_CB, /* Called in hardware phase when
|
||||
* SK_BPF_CB_TX_TIMESTAMPING feature
|
||||
* is on.
|
||||
*/
|
||||
BPF_SOCK_OPS_TSTAMP_ACK_CB, /* Called when all the skbs in the
|
||||
* same sendmsg call are acked
|
||||
* when SK_BPF_CB_TX_TIMESTAMPING
|
||||
* feature is on.
|
||||
*/
|
||||
BPF_SOCK_OPS_TSTAMP_SENDMSG_CB, /* Called when every sendmsg syscall
|
||||
* is triggered. It's used to correlate
|
||||
* sendmsg timestamp with corresponding
|
||||
* tskey.
|
||||
*/
|
||||
};
|
||||
|
||||
/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
|
||||
@@ -7091,6 +7120,7 @@ enum {
|
||||
TCP_BPF_SYN_IP = 1006, /* Copy the IP[46] and TCP header */
|
||||
TCP_BPF_SYN_MAC = 1007, /* Copy the MAC, IP[46], and TCP header */
|
||||
TCP_BPF_SOCK_OPS_CB_FLAGS = 1008, /* Get or Set TCP sock ops flags */
|
||||
SK_BPF_CB_FLAGS = 1009, /* Get or set sock ops flags in socket */
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -127,6 +127,12 @@ struct xdp_options {
|
||||
*/
|
||||
#define XDP_TXMD_FLAGS_CHECKSUM (1 << 1)
|
||||
|
||||
/* Request launch time hardware offload. The device will schedule the packet for
|
||||
* transmission at a pre-determined time called launch time. The value of
|
||||
* launch time is communicated via launch_time field of struct xsk_tx_metadata.
|
||||
*/
|
||||
#define XDP_TXMD_FLAGS_LAUNCH_TIME (1 << 2)
|
||||
|
||||
/* AF_XDP offloads request. 'request' union member is consumed by the driver
|
||||
* when the packet is being transmitted. 'completion' union member is
|
||||
* filled by the driver when the transmit completion arrives.
|
||||
@@ -142,6 +148,10 @@ struct xsk_tx_metadata {
|
||||
__u16 csum_start;
|
||||
/* Offset from csum_start where checksum should be stored. */
|
||||
__u16 csum_offset;
|
||||
|
||||
/* XDP_TXMD_FLAGS_LAUNCH_TIME */
|
||||
/* Launch time in nanosecond against the PTP HW Clock */
|
||||
__u64 launch_time;
|
||||
} request;
|
||||
|
||||
struct {
|
||||
|
||||
@@ -59,10 +59,13 @@ enum netdev_xdp_rx_metadata {
|
||||
* by the driver.
|
||||
* @NETDEV_XSK_FLAGS_TX_CHECKSUM: L3 checksum HW offload is supported by the
|
||||
* driver.
|
||||
* @NETDEV_XSK_FLAGS_TX_LAUNCH_TIME_FIFO: Launch time HW offload is supported
|
||||
* by the driver.
|
||||
*/
|
||||
enum netdev_xsk_flags {
|
||||
NETDEV_XSK_FLAGS_TX_TIMESTAMP = 1,
|
||||
NETDEV_XSK_FLAGS_TX_CHECKSUM = 2,
|
||||
NETDEV_XSK_FLAGS_TX_LAUNCH_TIME_FIFO = 4,
|
||||
};
|
||||
|
||||
enum netdev_queue_type {
|
||||
|
||||
@@ -8524,6 +8524,7 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
|
||||
case BPF_PROG_TYPE_CGROUP_SYSCTL:
|
||||
case BPF_PROG_TYPE_SOCK_OPS:
|
||||
return BTF_KFUNC_HOOK_CGROUP;
|
||||
case BPF_PROG_TYPE_SCHED_ACT:
|
||||
return BTF_KFUNC_HOOK_SCHED_ACT;
|
||||
|
||||
+2
-1
@@ -4572,7 +4572,8 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
|
||||
skb_reset_mac_header(skb);
|
||||
skb_assert_len(skb);
|
||||
|
||||
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_SCHED_TSTAMP))
|
||||
if (unlikely(skb_shinfo(skb)->tx_flags &
|
||||
(SKBTX_SCHED_TSTAMP | SKBTX_BPF)))
|
||||
__skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SCHED);
|
||||
|
||||
/* Disable soft irqs for various locks below. Also
|
||||
|
||||
+75
-5
@@ -5222,6 +5222,25 @@ static const struct bpf_func_proto bpf_get_socket_uid_proto = {
|
||||
.arg1_type = ARG_PTR_TO_CTX,
|
||||
};
|
||||
|
||||
static int sk_bpf_set_get_cb_flags(struct sock *sk, char *optval, bool getopt)
|
||||
{
|
||||
u32 sk_bpf_cb_flags;
|
||||
|
||||
if (getopt) {
|
||||
*(u32 *)optval = sk->sk_bpf_cb_flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sk_bpf_cb_flags = *(u32 *)optval;
|
||||
|
||||
if (sk_bpf_cb_flags & ~SK_BPF_CB_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
sk->sk_bpf_cb_flags = sk_bpf_cb_flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sol_socket_sockopt(struct sock *sk, int optname,
|
||||
char *optval, int *optlen,
|
||||
bool getopt)
|
||||
@@ -5238,6 +5257,7 @@ static int sol_socket_sockopt(struct sock *sk, int optname,
|
||||
case SO_MAX_PACING_RATE:
|
||||
case SO_BINDTOIFINDEX:
|
||||
case SO_TXREHASH:
|
||||
case SK_BPF_CB_FLAGS:
|
||||
if (*optlen != sizeof(int))
|
||||
return -EINVAL;
|
||||
break;
|
||||
@@ -5247,6 +5267,9 @@ static int sol_socket_sockopt(struct sock *sk, int optname,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (optname == SK_BPF_CB_FLAGS)
|
||||
return sk_bpf_set_get_cb_flags(sk, optval, getopt);
|
||||
|
||||
if (getopt) {
|
||||
if (optname == SO_BINDTODEVICE)
|
||||
return -EINVAL;
|
||||
@@ -5382,6 +5405,7 @@ static int sol_tcp_sockopt(struct sock *sk, int optname,
|
||||
case TCP_USER_TIMEOUT:
|
||||
case TCP_NOTSENT_LOWAT:
|
||||
case TCP_SAVE_SYN:
|
||||
case TCP_RTO_MAX_MS:
|
||||
if (*optlen != sizeof(int))
|
||||
return -EINVAL;
|
||||
break;
|
||||
@@ -5500,6 +5524,11 @@ static int __bpf_setsockopt(struct sock *sk, int level, int optname,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static bool is_locked_tcp_sock_ops(struct bpf_sock_ops_kern *bpf_sock)
|
||||
{
|
||||
return bpf_sock->op <= BPF_SOCK_OPS_WRITE_HDR_OPT_CB;
|
||||
}
|
||||
|
||||
static int _bpf_setsockopt(struct sock *sk, int level, int optname,
|
||||
char *optval, int optlen)
|
||||
{
|
||||
@@ -5650,6 +5679,9 @@ static const struct bpf_func_proto bpf_sock_addr_getsockopt_proto = {
|
||||
BPF_CALL_5(bpf_sock_ops_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
|
||||
int, level, int, optname, char *, optval, int, optlen)
|
||||
{
|
||||
if (!is_locked_tcp_sock_ops(bpf_sock))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return _bpf_setsockopt(bpf_sock->sk, level, optname, optval, optlen);
|
||||
}
|
||||
|
||||
@@ -5735,6 +5767,9 @@ static int bpf_sock_ops_get_syn(struct bpf_sock_ops_kern *bpf_sock,
|
||||
BPF_CALL_5(bpf_sock_ops_getsockopt, struct bpf_sock_ops_kern *, bpf_sock,
|
||||
int, level, int, optname, char *, optval, int, optlen)
|
||||
{
|
||||
if (!is_locked_tcp_sock_ops(bpf_sock))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (IS_ENABLED(CONFIG_INET) && level == SOL_TCP &&
|
||||
optname >= TCP_BPF_SYN && optname <= TCP_BPF_SYN_MAC) {
|
||||
int ret, copy_len = 0;
|
||||
@@ -5777,6 +5812,9 @@ BPF_CALL_2(bpf_sock_ops_cb_flags_set, struct bpf_sock_ops_kern *, bpf_sock,
|
||||
struct sock *sk = bpf_sock->sk;
|
||||
int val = argval & BPF_SOCK_OPS_ALL_CB_FLAGS;
|
||||
|
||||
if (!is_locked_tcp_sock_ops(bpf_sock))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_INET) || !sk_fullsock(sk))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -7586,6 +7624,9 @@ BPF_CALL_4(bpf_sock_ops_load_hdr_opt, struct bpf_sock_ops_kern *, bpf_sock,
|
||||
u8 search_kind, search_len, copy_len, magic_len;
|
||||
int ret;
|
||||
|
||||
if (!is_locked_tcp_sock_ops(bpf_sock))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* 2 byte is the minimal option len except TCPOPT_NOP and
|
||||
* TCPOPT_EOL which are useless for the bpf prog to learn
|
||||
* and this helper disallow loading them also.
|
||||
@@ -10358,10 +10399,10 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
|
||||
} \
|
||||
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( \
|
||||
struct bpf_sock_ops_kern, \
|
||||
is_fullsock), \
|
||||
is_locked_tcp_sock), \
|
||||
fullsock_reg, si->src_reg, \
|
||||
offsetof(struct bpf_sock_ops_kern, \
|
||||
is_fullsock)); \
|
||||
is_locked_tcp_sock)); \
|
||||
*insn++ = BPF_JMP_IMM(BPF_JEQ, fullsock_reg, 0, jmp); \
|
||||
if (si->dst_reg == si->src_reg) \
|
||||
*insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg, \
|
||||
@@ -10446,10 +10487,10 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
|
||||
temp)); \
|
||||
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( \
|
||||
struct bpf_sock_ops_kern, \
|
||||
is_fullsock), \
|
||||
is_locked_tcp_sock), \
|
||||
reg, si->dst_reg, \
|
||||
offsetof(struct bpf_sock_ops_kern, \
|
||||
is_fullsock)); \
|
||||
is_locked_tcp_sock)); \
|
||||
*insn++ = BPF_JMP_IMM(BPF_JEQ, reg, 0, 2); \
|
||||
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( \
|
||||
struct bpf_sock_ops_kern, sk),\
|
||||
@@ -12062,6 +12103,25 @@ __bpf_kfunc int bpf_sk_assign_tcp_reqsk(struct __sk_buff *s, struct sock *sk,
|
||||
#endif
|
||||
}
|
||||
|
||||
__bpf_kfunc int bpf_sock_ops_enable_tx_tstamp(struct bpf_sock_ops_kern *skops,
|
||||
u64 flags)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (skops->op != BPF_SOCK_OPS_TSTAMP_SENDMSG_CB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (flags)
|
||||
return -EINVAL;
|
||||
|
||||
skb = skops->skb;
|
||||
skb_shinfo(skb)->tx_flags |= SKBTX_BPF;
|
||||
TCP_SKB_CB(skb)->txstamp_ack |= TSTAMP_ACK_BPF;
|
||||
skb_shinfo(skb)->tskey = TCP_SKB_CB(skb)->seq + skb->len - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__bpf_kfunc_end_defs();
|
||||
|
||||
int bpf_dynptr_from_skb_rdonly(struct __sk_buff *skb, u64 flags,
|
||||
@@ -12095,6 +12155,10 @@ BTF_KFUNCS_START(bpf_kfunc_check_set_tcp_reqsk)
|
||||
BTF_ID_FLAGS(func, bpf_sk_assign_tcp_reqsk, KF_TRUSTED_ARGS)
|
||||
BTF_KFUNCS_END(bpf_kfunc_check_set_tcp_reqsk)
|
||||
|
||||
BTF_KFUNCS_START(bpf_kfunc_check_set_sock_ops)
|
||||
BTF_ID_FLAGS(func, bpf_sock_ops_enable_tx_tstamp, KF_TRUSTED_ARGS)
|
||||
BTF_KFUNCS_END(bpf_kfunc_check_set_sock_ops)
|
||||
|
||||
static const struct btf_kfunc_id_set bpf_kfunc_set_skb = {
|
||||
.owner = THIS_MODULE,
|
||||
.set = &bpf_kfunc_check_set_skb,
|
||||
@@ -12115,6 +12179,11 @@ static const struct btf_kfunc_id_set bpf_kfunc_set_tcp_reqsk = {
|
||||
.set = &bpf_kfunc_check_set_tcp_reqsk,
|
||||
};
|
||||
|
||||
static const struct btf_kfunc_id_set bpf_kfunc_set_sock_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set = &bpf_kfunc_check_set_sock_ops,
|
||||
};
|
||||
|
||||
static int __init bpf_kfunc_init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -12133,7 +12202,8 @@ static int __init bpf_kfunc_init(void)
|
||||
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &bpf_kfunc_set_xdp);
|
||||
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
|
||||
&bpf_kfunc_set_sock_addr);
|
||||
return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_kfunc_set_tcp_reqsk);
|
||||
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_kfunc_set_tcp_reqsk);
|
||||
return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SOCK_OPS, &bpf_kfunc_set_sock_ops);
|
||||
}
|
||||
late_initcall(bpf_kfunc_init);
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ XDP_METADATA_KFUNC_xxx
|
||||
xsk_features |= NETDEV_XSK_FLAGS_TX_TIMESTAMP;
|
||||
if (netdev->xsk_tx_metadata_ops->tmo_request_checksum)
|
||||
xsk_features |= NETDEV_XSK_FLAGS_TX_CHECKSUM;
|
||||
if (netdev->xsk_tx_metadata_ops->tmo_request_launch_time)
|
||||
xsk_features |= NETDEV_XSK_FLAGS_TX_LAUNCH_TIME_FIFO;
|
||||
}
|
||||
|
||||
if (nla_put_u32(rsp, NETDEV_A_DEV_IFINDEX, netdev->ifindex) ||
|
||||
|
||||
@@ -5449,6 +5449,52 @@ err:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
|
||||
|
||||
static bool skb_tstamp_tx_report_so_timestamping(struct sk_buff *skb,
|
||||
struct skb_shared_hwtstamps *hwtstamps,
|
||||
int tstype)
|
||||
{
|
||||
switch (tstype) {
|
||||
case SCM_TSTAMP_SCHED:
|
||||
return skb_shinfo(skb)->tx_flags & SKBTX_SCHED_TSTAMP;
|
||||
case SCM_TSTAMP_SND:
|
||||
return skb_shinfo(skb)->tx_flags & (hwtstamps ? SKBTX_HW_TSTAMP_NOBPF :
|
||||
SKBTX_SW_TSTAMP);
|
||||
case SCM_TSTAMP_ACK:
|
||||
return TCP_SKB_CB(skb)->txstamp_ack & TSTAMP_ACK_SK;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void skb_tstamp_tx_report_bpf_timestamping(struct sk_buff *skb,
|
||||
struct skb_shared_hwtstamps *hwtstamps,
|
||||
struct sock *sk,
|
||||
int tstype)
|
||||
{
|
||||
int op;
|
||||
|
||||
switch (tstype) {
|
||||
case SCM_TSTAMP_SCHED:
|
||||
op = BPF_SOCK_OPS_TSTAMP_SCHED_CB;
|
||||
break;
|
||||
case SCM_TSTAMP_SND:
|
||||
if (hwtstamps) {
|
||||
op = BPF_SOCK_OPS_TSTAMP_SND_HW_CB;
|
||||
*skb_hwtstamps(skb) = *hwtstamps;
|
||||
} else {
|
||||
op = BPF_SOCK_OPS_TSTAMP_SND_SW_CB;
|
||||
}
|
||||
break;
|
||||
case SCM_TSTAMP_ACK:
|
||||
op = BPF_SOCK_OPS_TSTAMP_ACK_CB;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
bpf_skops_tx_timestamping(sk, skb, op);
|
||||
}
|
||||
|
||||
void __skb_tstamp_tx(struct sk_buff *orig_skb,
|
||||
const struct sk_buff *ack_skb,
|
||||
struct skb_shared_hwtstamps *hwtstamps,
|
||||
@@ -5461,6 +5507,13 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb,
|
||||
if (!sk)
|
||||
return;
|
||||
|
||||
if (skb_shinfo(orig_skb)->tx_flags & SKBTX_BPF)
|
||||
skb_tstamp_tx_report_bpf_timestamping(orig_skb, hwtstamps,
|
||||
sk, tstype);
|
||||
|
||||
if (!skb_tstamp_tx_report_so_timestamping(orig_skb, hwtstamps, tstype))
|
||||
return;
|
||||
|
||||
tsflags = READ_ONCE(sk->sk_tsflags);
|
||||
if (!hwtstamps && !(tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) &&
|
||||
skb_shinfo(orig_skb)->tx_flags & SKBTX_IN_PROGRESS)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user