mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-06 11:26:22 -04:00
Merge branch 'eth-fbnic-add-timestamping-support'
Vadim Fedorenko says: ==================== eth: fbnic: add timestamping support The series is to add timestamping support for Meta's NIC driver. Changelog: v3 -> v4: - use adjust_by_scaled_ppm() instead of open coding it - adjust cached value of high bits of timestamp to be sure it is older then incoming timestamps v2 -> v3: - rebase on top of net-next - add doc to describe retur value of fbnic_ts40_to_ns() v1 -> v2: - adjust comment about using u64 stats locking primitive - fix typo in the first patch - Cc Richard ==================== Link: https://patch.msgid.link/20241008181436.4120604-1-vadfed@meta.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
@@ -18,4 +18,5 @@ fbnic-y := fbnic_devlink.o \
|
||||
fbnic_phylink.o \
|
||||
fbnic_rpc.o \
|
||||
fbnic_tlv.o \
|
||||
fbnic_txrx.o
|
||||
fbnic_txrx.o \
|
||||
fbnic_time.o
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
@@ -49,6 +50,16 @@ struct fbnic_dev {
|
||||
/* Number of TCQs/RCQs available on hardware */
|
||||
u16 max_num_queues;
|
||||
|
||||
/* Lock protecting writes to @time_high, @time_offset of fbnic_netdev,
|
||||
* and the HW time CSR machinery.
|
||||
*/
|
||||
spinlock_t time_lock;
|
||||
/* Externally accessible PTP clock, may be NULL */
|
||||
struct ptp_clock *ptp;
|
||||
struct ptp_clock_info ptp_info;
|
||||
/* Last @time_high refresh time in jiffies (to catch stalls) */
|
||||
unsigned long last_read;
|
||||
|
||||
/* Local copy of hardware statistics */
|
||||
struct fbnic_hw_stats hw_stats;
|
||||
};
|
||||
|
||||
@@ -413,6 +413,44 @@ enum {
|
||||
#define FBNIC_TMI_DROP_CTRL 0x04401 /* 0x11004 */
|
||||
#define FBNIC_TMI_DROP_CTRL_EN CSR_BIT(0)
|
||||
#define FBNIC_CSR_END_TMI 0x0443f /* CSR section delimiter */
|
||||
|
||||
/* Precision Time Protocol Registers */
|
||||
#define FBNIC_CSR_START_PTP 0x04800 /* CSR section delimiter */
|
||||
#define FBNIC_PTP_REG_BASE 0x04800 /* 0x12000 */
|
||||
|
||||
#define FBNIC_PTP_CTRL 0x04800 /* 0x12000 */
|
||||
#define FBNIC_PTP_CTRL_EN CSR_BIT(0)
|
||||
#define FBNIC_PTP_CTRL_MONO_EN CSR_BIT(4)
|
||||
#define FBNIC_PTP_CTRL_TQS_OUT_EN CSR_BIT(8)
|
||||
#define FBNIC_PTP_CTRL_MAC_OUT_IVAL CSR_GENMASK(16, 12)
|
||||
#define FBNIC_PTP_CTRL_TICK_IVAL CSR_GENMASK(23, 20)
|
||||
|
||||
#define FBNIC_PTP_ADJUST 0x04801 /* 0x12004 */
|
||||
#define FBNIC_PTP_ADJUST_INIT CSR_BIT(0)
|
||||
#define FBNIC_PTP_ADJUST_SUB_NUDGE CSR_BIT(8)
|
||||
#define FBNIC_PTP_ADJUST_ADD_NUDGE CSR_BIT(16)
|
||||
#define FBNIC_PTP_ADJUST_ADDEND_SET CSR_BIT(24)
|
||||
|
||||
#define FBNIC_PTP_INIT_HI 0x04802 /* 0x12008 */
|
||||
#define FBNIC_PTP_INIT_LO 0x04803 /* 0x1200c */
|
||||
|
||||
#define FBNIC_PTP_NUDGE_NS 0x04804 /* 0x12010 */
|
||||
#define FBNIC_PTP_NUDGE_SUBNS 0x04805 /* 0x12014 */
|
||||
|
||||
#define FBNIC_PTP_ADD_VAL_NS 0x04806 /* 0x12018 */
|
||||
#define FBNIC_PTP_ADD_VAL_NS_MASK CSR_GENMASK(15, 0)
|
||||
#define FBNIC_PTP_ADD_VAL_SUBNS 0x04807 /* 0x1201c */
|
||||
|
||||
#define FBNIC_PTP_CTR_VAL_HI 0x04808 /* 0x12020 */
|
||||
#define FBNIC_PTP_CTR_VAL_LO 0x04809 /* 0x12024 */
|
||||
|
||||
#define FBNIC_PTP_MONO_PTP_CTR_HI 0x0480a /* 0x12028 */
|
||||
#define FBNIC_PTP_MONO_PTP_CTR_LO 0x0480b /* 0x1202c */
|
||||
|
||||
#define FBNIC_PTP_CDC_FIFO_STATUS 0x0480c /* 0x12030 */
|
||||
#define FBNIC_PTP_SPARE 0x0480d /* 0x12034 */
|
||||
#define FBNIC_CSR_END_PTP 0x0480d /* CSR section delimiter */
|
||||
|
||||
/* Rx Buffer Registers */
|
||||
#define FBNIC_CSR_START_RXB 0x08000 /* CSR section delimiter */
|
||||
enum {
|
||||
@@ -548,6 +586,7 @@ enum {
|
||||
};
|
||||
|
||||
#define FBNIC_RPC_ACT_TBL0_DMA_HINT CSR_GENMASK(24, 16)
|
||||
#define FBNIC_RPC_ACT_TBL0_TS_ENA CSR_BIT(28)
|
||||
#define FBNIC_RPC_ACT_TBL0_RSS_CTXT_ID CSR_BIT(30)
|
||||
|
||||
#define FBNIC_RPC_ACT_TBL1_DEFAULT 0x0840b /* 0x2102c */
|
||||
|
||||
@@ -6,6 +6,35 @@
|
||||
#include "fbnic_netdev.h"
|
||||
#include "fbnic_tlv.h"
|
||||
|
||||
static int
|
||||
fbnic_get_ts_info(struct net_device *netdev,
|
||||
struct kernel_ethtool_ts_info *tsinfo)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
|
||||
tsinfo->phc_index = ptp_clock_index(fbn->fbd->ptp);
|
||||
|
||||
tsinfo->so_timestamping =
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_TX_HARDWARE |
|
||||
SOF_TIMESTAMPING_RX_HARDWARE |
|
||||
SOF_TIMESTAMPING_RAW_HARDWARE;
|
||||
|
||||
tsinfo->tx_types =
|
||||
BIT(HWTSTAMP_TX_OFF) |
|
||||
BIT(HWTSTAMP_TX_ON);
|
||||
|
||||
tsinfo->rx_filters =
|
||||
BIT(HWTSTAMP_FILTER_NONE) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
|
||||
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) |
|
||||
BIT(HWTSTAMP_FILTER_ALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fbnic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
|
||||
{
|
||||
@@ -64,8 +93,33 @@ fbnic_get_eth_mac_stats(struct net_device *netdev,
|
||||
&mac_stats->eth_mac.FrameTooLongErrors);
|
||||
}
|
||||
|
||||
static void fbnic_get_ts_stats(struct net_device *netdev,
|
||||
struct ethtool_ts_stats *ts_stats)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
u64 ts_packets, ts_lost;
|
||||
struct fbnic_ring *ring;
|
||||
unsigned int start;
|
||||
int i;
|
||||
|
||||
ts_stats->pkts = fbn->tx_stats.ts_packets;
|
||||
ts_stats->lost = fbn->tx_stats.ts_lost;
|
||||
for (i = 0; i < fbn->num_tx_queues; i++) {
|
||||
ring = fbn->tx[i];
|
||||
do {
|
||||
start = u64_stats_fetch_begin(&ring->stats.syncp);
|
||||
ts_packets = ring->stats.ts_packets;
|
||||
ts_lost = ring->stats.ts_lost;
|
||||
} while (u64_stats_fetch_retry(&ring->stats.syncp, start));
|
||||
ts_stats->pkts += ts_packets;
|
||||
ts_stats->lost += ts_lost;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ethtool_ops fbnic_ethtool_ops = {
|
||||
.get_drvinfo = fbnic_get_drvinfo,
|
||||
.get_ts_info = fbnic_get_ts_info,
|
||||
.get_ts_stats = fbnic_get_ts_stats,
|
||||
.get_eth_mac_stats = fbnic_get_eth_mac_stats,
|
||||
};
|
||||
|
||||
|
||||
@@ -42,18 +42,24 @@ int __fbnic_open(struct fbnic_net *fbn)
|
||||
goto free_resources;
|
||||
}
|
||||
|
||||
err = fbnic_fw_init_heartbeat(fbd, false);
|
||||
err = fbnic_time_start(fbn);
|
||||
if (err)
|
||||
goto release_ownership;
|
||||
|
||||
err = fbnic_fw_init_heartbeat(fbd, false);
|
||||
if (err)
|
||||
goto time_stop;
|
||||
|
||||
err = fbnic_pcs_irq_enable(fbd);
|
||||
if (err)
|
||||
goto release_ownership;
|
||||
goto time_stop;
|
||||
/* Pull the BMC config and initialize the RPC */
|
||||
fbnic_bmc_rpc_init(fbd);
|
||||
fbnic_rss_reinit(fbd, fbn);
|
||||
|
||||
return 0;
|
||||
time_stop:
|
||||
fbnic_time_stop(fbn);
|
||||
release_ownership:
|
||||
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
|
||||
free_resources:
|
||||
@@ -82,6 +88,7 @@ static int fbnic_stop(struct net_device *netdev)
|
||||
fbnic_down(fbn);
|
||||
fbnic_pcs_irq_disable(fbn->fbd);
|
||||
|
||||
fbnic_time_stop(fbn);
|
||||
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
|
||||
|
||||
fbnic_free_resources(fbn);
|
||||
@@ -317,6 +324,84 @@ void fbnic_clear_rx_mode(struct net_device *netdev)
|
||||
__dev_mc_unsync(netdev, NULL);
|
||||
}
|
||||
|
||||
static int fbnic_hwtstamp_get(struct net_device *netdev,
|
||||
struct kernel_hwtstamp_config *config)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
|
||||
*config = fbn->hwtstamp_config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fbnic_hwtstamp_set(struct net_device *netdev,
|
||||
struct kernel_hwtstamp_config *config,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
int old_rx_filter;
|
||||
|
||||
if (config->source != HWTSTAMP_SOURCE_NETDEV)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!kernel_hwtstamp_config_changed(config, &fbn->hwtstamp_config))
|
||||
return 0;
|
||||
|
||||
/* Upscale the filters */
|
||||
switch (config->rx_filter) {
|
||||
case HWTSTAMP_FILTER_NONE:
|
||||
case HWTSTAMP_FILTER_ALL:
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
||||
break;
|
||||
case HWTSTAMP_FILTER_NTP_ALL:
|
||||
config->rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
break;
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
||||
config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
|
||||
break;
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
||||
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
|
||||
break;
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
||||
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
|
||||
break;
|
||||
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
||||
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
||||
break;
|
||||
default:
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Configure */
|
||||
old_rx_filter = fbn->hwtstamp_config.rx_filter;
|
||||
memcpy(&fbn->hwtstamp_config, config, sizeof(*config));
|
||||
|
||||
if (old_rx_filter != config->rx_filter && netif_running(fbn->netdev)) {
|
||||
fbnic_rss_reinit(fbn->fbd, fbn);
|
||||
fbnic_write_rules(fbn->fbd);
|
||||
}
|
||||
|
||||
/* Save / report back filter configuration
|
||||
* Note that our filter configuration is inexact. Instead of
|
||||
* filtering for a specific UDP port or L2 Ethertype we are
|
||||
* filtering in all UDP or all non-IP packets for timestamping. So
|
||||
* if anything other than FILTER_ALL is requested we report
|
||||
* FILTER_SOME indicating that we will be timestamping a few
|
||||
* additional packets.
|
||||
*/
|
||||
if (config->rx_filter > HWTSTAMP_FILTER_ALL)
|
||||
config->rx_filter = HWTSTAMP_FILTER_SOME;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fbnic_get_stats64(struct net_device *dev,
|
||||
struct rtnl_link_stats64 *stats64)
|
||||
{
|
||||
@@ -394,6 +479,8 @@ static const struct net_device_ops fbnic_netdev_ops = {
|
||||
.ndo_set_mac_address = fbnic_set_mac,
|
||||
.ndo_set_rx_mode = fbnic_set_rx_mode,
|
||||
.ndo_get_stats64 = fbnic_get_stats64,
|
||||
.ndo_hwtstamp_get = fbnic_hwtstamp_get,
|
||||
.ndo_hwtstamp_set = fbnic_hwtstamp_set,
|
||||
};
|
||||
|
||||
static void fbnic_get_queue_stats_rx(struct net_device *dev, int idx,
|
||||
|
||||
@@ -33,6 +33,15 @@ struct fbnic_net {
|
||||
u8 fec;
|
||||
u8 link_mode;
|
||||
|
||||
/* Cached top bits of the HW time counter for 40b -> 64b conversion */
|
||||
u32 time_high;
|
||||
/* Protect readers of @time_offset, writers take @time_lock. */
|
||||
struct u64_stats_sync time_seq;
|
||||
/* Offset in ns between free running NIC PHC and time set via PTP
|
||||
* clock callbacks
|
||||
*/
|
||||
s64 time_offset;
|
||||
|
||||
u16 num_tx_queues;
|
||||
u16 num_rx_queues;
|
||||
|
||||
@@ -45,6 +54,9 @@ struct fbnic_net {
|
||||
struct fbnic_queue_stats rx_stats;
|
||||
u64 link_down_events;
|
||||
|
||||
/* Time stampinn filter config */
|
||||
struct kernel_hwtstamp_config hwtstamp_config;
|
||||
|
||||
struct list_head napis;
|
||||
};
|
||||
|
||||
@@ -60,6 +72,12 @@ void fbnic_reset_queues(struct fbnic_net *fbn,
|
||||
unsigned int tx, unsigned int rx);
|
||||
void fbnic_set_ethtool_ops(struct net_device *dev);
|
||||
|
||||
int fbnic_ptp_setup(struct fbnic_dev *fbd);
|
||||
void fbnic_ptp_destroy(struct fbnic_dev *fbd);
|
||||
void fbnic_time_init(struct fbnic_net *fbn);
|
||||
int fbnic_time_start(struct fbnic_net *fbn);
|
||||
void fbnic_time_stop(struct fbnic_net *fbn);
|
||||
|
||||
void __fbnic_set_rx_mode(struct net_device *netdev);
|
||||
void fbnic_clear_rx_mode(struct net_device *netdev);
|
||||
|
||||
|
||||
@@ -300,14 +300,20 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
goto init_failure_mode;
|
||||
}
|
||||
|
||||
err = fbnic_ptp_setup(fbd);
|
||||
if (err)
|
||||
goto ifm_free_netdev;
|
||||
|
||||
err = fbnic_netdev_register(netdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Netdev registration failed: %d\n", err);
|
||||
goto ifm_free_netdev;
|
||||
goto ifm_destroy_ptp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ifm_destroy_ptp:
|
||||
fbnic_ptp_destroy(fbd);
|
||||
ifm_free_netdev:
|
||||
fbnic_netdev_free(fbd);
|
||||
init_failure_mode:
|
||||
@@ -342,6 +348,7 @@ static void fbnic_remove(struct pci_dev *pdev)
|
||||
|
||||
fbnic_netdev_unregister(netdev);
|
||||
cancel_delayed_work_sync(&fbd->service_task);
|
||||
fbnic_ptp_destroy(fbd);
|
||||
fbnic_netdev_free(fbd);
|
||||
}
|
||||
|
||||
|
||||
@@ -244,6 +244,12 @@ void fbnic_bmc_rpc_init(struct fbnic_dev *fbd)
|
||||
((_ip) ? FBNIC_RPC_TCAM_ACT1_IP_VALID : 0) | \
|
||||
((_v6) ? FBNIC_RPC_TCAM_ACT1_IP_IS_V6 : 0))
|
||||
|
||||
#define FBNIC_TSTAMP_MASK(_all, _udp, _ether) \
|
||||
(((_all) ? ((1u << FBNIC_NUM_HASH_OPT) - 1) : 0) | \
|
||||
((_udp) ? (1u << FBNIC_UDP6_HASH_OPT) | \
|
||||
(1u << FBNIC_UDP4_HASH_OPT) : 0) | \
|
||||
((_ether) ? (1u << FBNIC_ETHER_HASH_OPT) : 0))
|
||||
|
||||
void fbnic_rss_reinit(struct fbnic_dev *fbd, struct fbnic_net *fbn)
|
||||
{
|
||||
static const u32 act1_value[FBNIC_NUM_HASH_OPT] = {
|
||||
@@ -255,6 +261,7 @@ void fbnic_rss_reinit(struct fbnic_dev *fbd, struct fbnic_net *fbn)
|
||||
FBNIC_ACT1_INIT(0, 0, 1, 0), /* IP4 */
|
||||
0 /* Ether */
|
||||
};
|
||||
u32 tstamp_mask = 0;
|
||||
unsigned int i;
|
||||
|
||||
/* To support scenarios where a BMC is present we must write the
|
||||
@@ -264,6 +271,28 @@ void fbnic_rss_reinit(struct fbnic_dev *fbd, struct fbnic_net *fbn)
|
||||
BUILD_BUG_ON(FBNIC_RSS_EN_NUM_UNICAST * 2 != FBNIC_RSS_EN_NUM_ENTRIES);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(act1_value) != FBNIC_NUM_HASH_OPT);
|
||||
|
||||
/* Set timestamp mask with 1b per flow type */
|
||||
if (fbn->hwtstamp_config.rx_filter != HWTSTAMP_FILTER_NONE) {
|
||||
switch (fbn->hwtstamp_config.rx_filter) {
|
||||
case HWTSTAMP_FILTER_ALL:
|
||||
tstamp_mask = FBNIC_TSTAMP_MASK(1, 1, 1);
|
||||
break;
|
||||
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
||||
tstamp_mask = FBNIC_TSTAMP_MASK(0, 1, 1);
|
||||
break;
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
||||
tstamp_mask = FBNIC_TSTAMP_MASK(0, 1, 0);
|
||||
break;
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
||||
tstamp_mask = FBNIC_TSTAMP_MASK(0, 0, 1);
|
||||
break;
|
||||
default:
|
||||
netdev_warn(fbn->netdev, "Unsupported hwtstamp_rx_filter\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Program RSS hash enable mask for host in action TCAM/table. */
|
||||
for (i = fbnic_bmc_present(fbd) ? 0 : FBNIC_RSS_EN_NUM_UNICAST;
|
||||
i < FBNIC_RSS_EN_NUM_ENTRIES; i++) {
|
||||
@@ -287,6 +316,8 @@ void fbnic_rss_reinit(struct fbnic_dev *fbd, struct fbnic_net *fbn)
|
||||
|
||||
if (!dest)
|
||||
dest = FBNIC_RPC_ACT_TBL0_DROP;
|
||||
else if (tstamp_mask & (1u << flow_type))
|
||||
dest |= FBNIC_RPC_ACT_TBL0_TS_ENA;
|
||||
|
||||
if (act1_value[flow_type] & FBNIC_RPC_TCAM_ACT1_L4_VALID)
|
||||
dest |= FIELD_PREP(FBNIC_RPC_ACT_TBL0_DMA_HINT,
|
||||
|
||||
303
drivers/net/ethernet/meta/fbnic/fbnic_time.c
Normal file
303
drivers/net/ethernet/meta/fbnic/fbnic_time.c
Normal file
@@ -0,0 +1,303 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include "fbnic.h"
|
||||
#include "fbnic_csr.h"
|
||||
#include "fbnic_netdev.h"
|
||||
|
||||
/* FBNIC timing & PTP implementation
|
||||
* Datapath uses truncated 40b timestamps for scheduling and event reporting.
|
||||
* We need to promote those to full 64b, hence we periodically cache the top
|
||||
* 32bit of the HW time counter. Since this makes our time reporting non-atomic
|
||||
* we leave the HW clock free running and adjust time offsets in SW as needed.
|
||||
* Time offset is 64bit - we need a seq counter for 32bit machines.
|
||||
* Time offset and the cache of top bits are independent so we don't need
|
||||
* a coherent snapshot of both - READ_ONCE()/WRITE_ONCE() + writer side lock
|
||||
* are enough.
|
||||
*/
|
||||
|
||||
/* Period of refresh of top bits of timestamp, give ourselves a 8x margin.
|
||||
* This should translate to once a minute.
|
||||
* The use of nsecs_to_jiffies() should be safe for a <=40b nsec value.
|
||||
*/
|
||||
#define FBNIC_TS_HIGH_REFRESH_JIF nsecs_to_jiffies((1ULL << 40) / 16)
|
||||
|
||||
static struct fbnic_dev *fbnic_from_ptp_info(struct ptp_clock_info *ptp)
|
||||
{
|
||||
return container_of(ptp, struct fbnic_dev, ptp_info);
|
||||
}
|
||||
|
||||
/* This function is "slow" because we could try guessing which high part
|
||||
* is correct based on low instead of re-reading, and skip reading @hi
|
||||
* twice altogether if @lo is far enough from 0.
|
||||
*/
|
||||
static u64 __fbnic_time_get_slow(struct fbnic_dev *fbd)
|
||||
{
|
||||
u32 hi, lo;
|
||||
|
||||
lockdep_assert_held(&fbd->time_lock);
|
||||
|
||||
do {
|
||||
hi = fbnic_rd32(fbd, FBNIC_PTP_CTR_VAL_HI);
|
||||
lo = fbnic_rd32(fbd, FBNIC_PTP_CTR_VAL_LO);
|
||||
} while (hi != fbnic_rd32(fbd, FBNIC_PTP_CTR_VAL_HI));
|
||||
|
||||
return (u64)hi << 32 | lo;
|
||||
}
|
||||
|
||||
static void __fbnic_time_set_addend(struct fbnic_dev *fbd, u64 addend)
|
||||
{
|
||||
lockdep_assert_held(&fbd->time_lock);
|
||||
|
||||
fbnic_wr32(fbd, FBNIC_PTP_ADD_VAL_NS,
|
||||
FIELD_PREP(FBNIC_PTP_ADD_VAL_NS_MASK, addend >> 32));
|
||||
fbnic_wr32(fbd, FBNIC_PTP_ADD_VAL_SUBNS, (u32)addend);
|
||||
}
|
||||
|
||||
static void fbnic_ptp_fresh_check(struct fbnic_dev *fbd)
|
||||
{
|
||||
if (time_is_after_jiffies(fbd->last_read +
|
||||
FBNIC_TS_HIGH_REFRESH_JIF * 3 / 2))
|
||||
return;
|
||||
|
||||
dev_warn(fbd->dev, "NIC timestamp refresh stall, delayed by %lu sec\n",
|
||||
(jiffies - fbd->last_read - FBNIC_TS_HIGH_REFRESH_JIF) / HZ);
|
||||
}
|
||||
|
||||
static void fbnic_ptp_refresh_time(struct fbnic_dev *fbd, struct fbnic_net *fbn)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 hi;
|
||||
|
||||
spin_lock_irqsave(&fbd->time_lock, flags);
|
||||
hi = fbnic_rd32(fbn->fbd, FBNIC_PTP_CTR_VAL_HI);
|
||||
if (!fbnic_present(fbd))
|
||||
goto out; /* Don't bother handling, reset is pending */
|
||||
/* Let's keep high cached value a bit lower to avoid race with
|
||||
* incoming timestamps. The logic in fbnic_ts40_to_ns() will
|
||||
* take care of overflow in this case. It will make cached time
|
||||
* ~1 minute lower and incoming timestamp will always be later
|
||||
* then cached time.
|
||||
*/
|
||||
WRITE_ONCE(fbn->time_high, hi - 16);
|
||||
fbd->last_read = jiffies;
|
||||
out:
|
||||
spin_unlock_irqrestore(&fbd->time_lock, flags);
|
||||
}
|
||||
|
||||
static long fbnic_ptp_do_aux_work(struct ptp_clock_info *ptp)
|
||||
{
|
||||
struct fbnic_dev *fbd = fbnic_from_ptp_info(ptp);
|
||||
struct fbnic_net *fbn;
|
||||
|
||||
fbn = netdev_priv(fbd->netdev);
|
||||
|
||||
fbnic_ptp_fresh_check(fbd);
|
||||
fbnic_ptp_refresh_time(fbd, fbn);
|
||||
|
||||
return FBNIC_TS_HIGH_REFRESH_JIF;
|
||||
}
|
||||
|
||||
static int fbnic_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
||||
{
|
||||
struct fbnic_dev *fbd = fbnic_from_ptp_info(ptp);
|
||||
u64 addend, dclk_period;
|
||||
unsigned long flags;
|
||||
|
||||
/* d_clock is 600 MHz; which in Q16.32 fixed point ns is: */
|
||||
dclk_period = (((u64)1000000000) << 32) / FBNIC_CLOCK_FREQ;
|
||||
addend = adjust_by_scaled_ppm(dclk_period, scaled_ppm);
|
||||
|
||||
spin_lock_irqsave(&fbd->time_lock, flags);
|
||||
__fbnic_time_set_addend(fbd, addend);
|
||||
fbnic_wr32(fbd, FBNIC_PTP_ADJUST, FBNIC_PTP_ADJUST_ADDEND_SET);
|
||||
|
||||
/* Flush, make sure FBNIC_PTP_ADD_VAL_* is stable for at least 4 clks */
|
||||
fbnic_rd32(fbd, FBNIC_PTP_SPARE);
|
||||
spin_unlock_irqrestore(&fbd->time_lock, flags);
|
||||
|
||||
return fbnic_present(fbd) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int fbnic_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
struct fbnic_dev *fbd = fbnic_from_ptp_info(ptp);
|
||||
struct fbnic_net *fbn;
|
||||
unsigned long flags;
|
||||
|
||||
fbn = netdev_priv(fbd->netdev);
|
||||
|
||||
spin_lock_irqsave(&fbd->time_lock, flags);
|
||||
u64_stats_update_begin(&fbn->time_seq);
|
||||
WRITE_ONCE(fbn->time_offset, READ_ONCE(fbn->time_offset) + delta);
|
||||
u64_stats_update_end(&fbn->time_seq);
|
||||
spin_unlock_irqrestore(&fbd->time_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fbnic_ptp_gettimex64(struct ptp_clock_info *ptp, struct timespec64 *ts,
|
||||
struct ptp_system_timestamp *sts)
|
||||
{
|
||||
struct fbnic_dev *fbd = fbnic_from_ptp_info(ptp);
|
||||
struct fbnic_net *fbn;
|
||||
unsigned long flags;
|
||||
u64 time_ns;
|
||||
u32 hi, lo;
|
||||
|
||||
fbn = netdev_priv(fbd->netdev);
|
||||
|
||||
spin_lock_irqsave(&fbd->time_lock, flags);
|
||||
|
||||
do {
|
||||
hi = fbnic_rd32(fbd, FBNIC_PTP_CTR_VAL_HI);
|
||||
ptp_read_system_prets(sts);
|
||||
lo = fbnic_rd32(fbd, FBNIC_PTP_CTR_VAL_LO);
|
||||
ptp_read_system_postts(sts);
|
||||
/* Similarly to comment above __fbnic_time_get_slow()
|
||||
* - this can be optimized if needed.
|
||||
*/
|
||||
} while (hi != fbnic_rd32(fbd, FBNIC_PTP_CTR_VAL_HI));
|
||||
|
||||
time_ns = ((u64)hi << 32 | lo) + fbn->time_offset;
|
||||
spin_unlock_irqrestore(&fbd->time_lock, flags);
|
||||
|
||||
if (!fbnic_present(fbd))
|
||||
return -EIO;
|
||||
|
||||
*ts = ns_to_timespec64(time_ns);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fbnic_ptp_settime64(struct ptp_clock_info *ptp, const struct timespec64 *ts)
|
||||
{
|
||||
struct fbnic_dev *fbd = fbnic_from_ptp_info(ptp);
|
||||
struct fbnic_net *fbn;
|
||||
unsigned long flags;
|
||||
u64 dev_ns, host_ns;
|
||||
int ret;
|
||||
|
||||
fbn = netdev_priv(fbd->netdev);
|
||||
|
||||
host_ns = timespec64_to_ns(ts);
|
||||
|
||||
spin_lock_irqsave(&fbd->time_lock, flags);
|
||||
|
||||
dev_ns = __fbnic_time_get_slow(fbd);
|
||||
|
||||
if (fbnic_present(fbd)) {
|
||||
u64_stats_update_begin(&fbn->time_seq);
|
||||
WRITE_ONCE(fbn->time_offset, host_ns - dev_ns);
|
||||
u64_stats_update_end(&fbn->time_seq);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
}
|
||||
spin_unlock_irqrestore(&fbd->time_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct ptp_clock_info fbnic_ptp_info = {
|
||||
.owner = THIS_MODULE,
|
||||
/* 1,000,000,000 - 1 PPB to ensure increment is positive
|
||||
* after max negative adjustment.
|
||||
*/
|
||||
.max_adj = 999999999,
|
||||
.do_aux_work = fbnic_ptp_do_aux_work,
|
||||
.adjfine = fbnic_ptp_adjfine,
|
||||
.adjtime = fbnic_ptp_adjtime,
|
||||
.gettimex64 = fbnic_ptp_gettimex64,
|
||||
.settime64 = fbnic_ptp_settime64,
|
||||
};
|
||||
|
||||
static void fbnic_ptp_reset(struct fbnic_dev *fbd)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(fbd->netdev);
|
||||
u64 dclk_period;
|
||||
|
||||
fbnic_wr32(fbd, FBNIC_PTP_CTRL,
|
||||
FBNIC_PTP_CTRL_EN |
|
||||
FIELD_PREP(FBNIC_PTP_CTRL_TICK_IVAL, 1));
|
||||
|
||||
/* d_clock is 600 MHz; which in Q16.32 fixed point ns is: */
|
||||
dclk_period = (((u64)1000000000) << 32) / FBNIC_CLOCK_FREQ;
|
||||
|
||||
__fbnic_time_set_addend(fbd, dclk_period);
|
||||
|
||||
fbnic_wr32(fbd, FBNIC_PTP_INIT_HI, 0);
|
||||
fbnic_wr32(fbd, FBNIC_PTP_INIT_LO, 0);
|
||||
|
||||
fbnic_wr32(fbd, FBNIC_PTP_ADJUST, FBNIC_PTP_ADJUST_INIT);
|
||||
|
||||
fbnic_wr32(fbd, FBNIC_PTP_CTRL,
|
||||
FBNIC_PTP_CTRL_EN |
|
||||
FBNIC_PTP_CTRL_TQS_OUT_EN |
|
||||
FIELD_PREP(FBNIC_PTP_CTRL_MAC_OUT_IVAL, 3) |
|
||||
FIELD_PREP(FBNIC_PTP_CTRL_TICK_IVAL, 1));
|
||||
|
||||
fbnic_rd32(fbd, FBNIC_PTP_SPARE);
|
||||
|
||||
fbn->time_offset = 0;
|
||||
fbn->time_high = 0;
|
||||
}
|
||||
|
||||
void fbnic_time_init(struct fbnic_net *fbn)
|
||||
{
|
||||
/* This is not really a statistic, but the lockng primitive fits
|
||||
* our usecase perfectly, we need an atomic 8 bytes READ_ONCE() /
|
||||
* WRITE_ONCE() behavior.
|
||||
*/
|
||||
u64_stats_init(&fbn->time_seq);
|
||||
}
|
||||
|
||||
int fbnic_time_start(struct fbnic_net *fbn)
|
||||
{
|
||||
fbnic_ptp_refresh_time(fbn->fbd, fbn);
|
||||
/* Assume that fbnic_ptp_do_aux_work() will never be called if not
|
||||
* scheduled here
|
||||
*/
|
||||
return ptp_schedule_worker(fbn->fbd->ptp, FBNIC_TS_HIGH_REFRESH_JIF);
|
||||
}
|
||||
|
||||
void fbnic_time_stop(struct fbnic_net *fbn)
|
||||
{
|
||||
ptp_cancel_worker_sync(fbn->fbd->ptp);
|
||||
fbnic_ptp_fresh_check(fbn->fbd);
|
||||
}
|
||||
|
||||
int fbnic_ptp_setup(struct fbnic_dev *fbd)
|
||||
{
|
||||
struct device *dev = fbd->dev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_init(&fbd->time_lock);
|
||||
|
||||
spin_lock_irqsave(&fbd->time_lock, flags); /* Appease lockdep */
|
||||
fbnic_ptp_reset(fbd);
|
||||
spin_unlock_irqrestore(&fbd->time_lock, flags);
|
||||
|
||||
memcpy(&fbd->ptp_info, &fbnic_ptp_info, sizeof(fbnic_ptp_info));
|
||||
|
||||
fbd->ptp = ptp_clock_register(&fbd->ptp_info, dev);
|
||||
if (IS_ERR(fbd->ptp))
|
||||
dev_err(dev, "Failed to register PTP: %pe\n", fbd->ptp);
|
||||
|
||||
return PTR_ERR_OR_ZERO(fbd->ptp);
|
||||
}
|
||||
|
||||
void fbnic_ptp_destroy(struct fbnic_dev *fbd)
|
||||
{
|
||||
if (!fbd->ptp)
|
||||
return;
|
||||
ptp_clock_unregister(fbd->ptp);
|
||||
}
|
||||
@@ -12,9 +12,14 @@
|
||||
#include "fbnic_netdev.h"
|
||||
#include "fbnic_txrx.h"
|
||||
|
||||
enum {
|
||||
FBNIC_XMIT_CB_TS = 0x01,
|
||||
};
|
||||
|
||||
struct fbnic_xmit_cb {
|
||||
u32 bytecount;
|
||||
u8 desc_count;
|
||||
u8 flags;
|
||||
int hw_head;
|
||||
};
|
||||
|
||||
@@ -43,6 +48,46 @@ static void fbnic_ring_wr32(struct fbnic_ring *ring, unsigned int csr, u32 val)
|
||||
writel(val, csr_base + csr);
|
||||
}
|
||||
|
||||
/**
|
||||
* fbnic_ts40_to_ns() - convert descriptor timestamp to PHC time
|
||||
* @fbn: netdev priv of the FB NIC
|
||||
* @ts40: timestamp read from a descriptor
|
||||
*
|
||||
* Return: u64 value of PHC time in nanoseconds
|
||||
*
|
||||
* Convert truncated 40 bit device timestamp as read from a descriptor
|
||||
* to the full PHC time in nanoseconds.
|
||||
*/
|
||||
static __maybe_unused u64 fbnic_ts40_to_ns(struct fbnic_net *fbn, u64 ts40)
|
||||
{
|
||||
unsigned int s;
|
||||
u64 time_ns;
|
||||
s64 offset;
|
||||
u8 ts_top;
|
||||
u32 high;
|
||||
|
||||
do {
|
||||
s = u64_stats_fetch_begin(&fbn->time_seq);
|
||||
offset = READ_ONCE(fbn->time_offset);
|
||||
} while (u64_stats_fetch_retry(&fbn->time_seq, s));
|
||||
|
||||
high = READ_ONCE(fbn->time_high);
|
||||
|
||||
/* Bits 63..40 from periodic clock reads, 39..0 from ts40 */
|
||||
time_ns = (u64)(high >> 8) << 40 | ts40;
|
||||
|
||||
/* Compare bits 32-39 between periodic reads and ts40,
|
||||
* see if HW clock may have wrapped since last read. We are sure
|
||||
* that periodic reads are always at least ~1 minute behind, so
|
||||
* this logic works perfectly fine.
|
||||
*/
|
||||
ts_top = ts40 >> 32;
|
||||
if (ts_top < (u8)high && (u8)high - ts_top > U8_MAX / 2)
|
||||
time_ns += 1ULL << 40;
|
||||
|
||||
return time_ns + offset;
|
||||
}
|
||||
|
||||
static unsigned int fbnic_desc_unused(struct fbnic_ring *ring)
|
||||
{
|
||||
return (ring->head - ring->tail - 1) & ring->size_mask;
|
||||
@@ -110,11 +155,32 @@ static void fbnic_unmap_page_twd(struct device *dev, __le64 *twd)
|
||||
#define FBNIC_TWD_TYPE(_type) \
|
||||
cpu_to_le64(FIELD_PREP(FBNIC_TWD_TYPE_MASK, FBNIC_TWD_TYPE_##_type))
|
||||
|
||||
static bool fbnic_tx_tstamp(struct sk_buff *skb)
|
||||
{
|
||||
struct fbnic_net *fbn;
|
||||
|
||||
if (!unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
|
||||
return false;
|
||||
|
||||
fbn = netdev_priv(skb->dev);
|
||||
if (fbn->hwtstamp_config.tx_type == HWTSTAMP_TX_OFF)
|
||||
return false;
|
||||
|
||||
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
||||
FBNIC_XMIT_CB(skb)->flags |= FBNIC_XMIT_CB_TS;
|
||||
FBNIC_XMIT_CB(skb)->hw_head = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta)
|
||||
{
|
||||
unsigned int l2len, i3len;
|
||||
|
||||
if (fbnic_tx_tstamp(skb))
|
||||
*meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_TS);
|
||||
|
||||
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL))
|
||||
return false;
|
||||
|
||||
@@ -205,6 +271,9 @@ fbnic_tx_map(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta)
|
||||
|
||||
ring->tail = tail;
|
||||
|
||||
/* Record SW timestamp */
|
||||
skb_tx_timestamp(skb);
|
||||
|
||||
/* Verify there is room for another packet */
|
||||
fbnic_maybe_stop_tx(skb->dev, ring, FBNIC_MAX_SKB_DESC);
|
||||
|
||||
@@ -316,7 +385,7 @@ static void fbnic_clean_twq0(struct fbnic_napi_vector *nv, int napi_budget,
|
||||
struct fbnic_ring *ring, bool discard,
|
||||
unsigned int hw_head)
|
||||
{
|
||||
u64 total_bytes = 0, total_packets = 0;
|
||||
u64 total_bytes = 0, total_packets = 0, ts_lost = 0;
|
||||
unsigned int head = ring->head;
|
||||
struct netdev_queue *txq;
|
||||
unsigned int clean_desc;
|
||||
@@ -331,6 +400,13 @@ static void fbnic_clean_twq0(struct fbnic_napi_vector *nv, int napi_budget,
|
||||
if (desc_cnt > clean_desc)
|
||||
break;
|
||||
|
||||
if (unlikely(FBNIC_XMIT_CB(skb)->flags & FBNIC_XMIT_CB_TS)) {
|
||||
FBNIC_XMIT_CB(skb)->hw_head = hw_head;
|
||||
if (likely(!discard))
|
||||
break;
|
||||
ts_lost++;
|
||||
}
|
||||
|
||||
ring->tx_buf[head] = NULL;
|
||||
|
||||
clean_desc -= desc_cnt;
|
||||
@@ -368,6 +444,7 @@ static void fbnic_clean_twq0(struct fbnic_napi_vector *nv, int napi_budget,
|
||||
if (unlikely(discard)) {
|
||||
u64_stats_update_begin(&ring->stats.syncp);
|
||||
ring->stats.dropped += total_packets;
|
||||
ring->stats.ts_lost += ts_lost;
|
||||
u64_stats_update_end(&ring->stats.syncp);
|
||||
|
||||
netdev_tx_completed_queue(txq, total_packets, total_bytes);
|
||||
@@ -384,6 +461,56 @@ static void fbnic_clean_twq0(struct fbnic_napi_vector *nv, int napi_budget,
|
||||
FBNIC_TX_DESC_WAKEUP);
|
||||
}
|
||||
|
||||
static void fbnic_clean_tsq(struct fbnic_napi_vector *nv,
|
||||
struct fbnic_ring *ring,
|
||||
u64 tcd, int *ts_head, int *head0)
|
||||
{
|
||||
struct skb_shared_hwtstamps hwtstamp;
|
||||
struct fbnic_net *fbn;
|
||||
struct sk_buff *skb;
|
||||
int head;
|
||||
u64 ns;
|
||||
|
||||
head = (*ts_head < 0) ? ring->head : *ts_head;
|
||||
|
||||
do {
|
||||
unsigned int desc_cnt;
|
||||
|
||||
if (head == ring->tail) {
|
||||
if (unlikely(net_ratelimit()))
|
||||
netdev_err(nv->napi.dev,
|
||||
"Tx timestamp without matching packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
skb = ring->tx_buf[head];
|
||||
desc_cnt = FBNIC_XMIT_CB(skb)->desc_count;
|
||||
|
||||
head += desc_cnt;
|
||||
head &= ring->size_mask;
|
||||
} while (!(FBNIC_XMIT_CB(skb)->flags & FBNIC_XMIT_CB_TS));
|
||||
|
||||
fbn = netdev_priv(nv->napi.dev);
|
||||
ns = fbnic_ts40_to_ns(fbn, FIELD_GET(FBNIC_TCD_TYPE1_TS_MASK, tcd));
|
||||
|
||||
memset(&hwtstamp, 0, sizeof(hwtstamp));
|
||||
hwtstamp.hwtstamp = ns_to_ktime(ns);
|
||||
|
||||
*ts_head = head;
|
||||
|
||||
FBNIC_XMIT_CB(skb)->flags &= ~FBNIC_XMIT_CB_TS;
|
||||
if (*head0 < 0) {
|
||||
head = FBNIC_XMIT_CB(skb)->hw_head;
|
||||
if (head >= 0)
|
||||
*head0 = head;
|
||||
}
|
||||
|
||||
skb_tstamp_tx(skb, &hwtstamp);
|
||||
u64_stats_update_begin(&ring->stats.syncp);
|
||||
ring->stats.ts_packets++;
|
||||
u64_stats_update_end(&ring->stats.syncp);
|
||||
}
|
||||
|
||||
static void fbnic_page_pool_init(struct fbnic_ring *ring, unsigned int idx,
|
||||
struct page *page)
|
||||
{
|
||||
@@ -417,10 +544,12 @@ static void fbnic_page_pool_drain(struct fbnic_ring *ring, unsigned int idx,
|
||||
}
|
||||
|
||||
static void fbnic_clean_twq(struct fbnic_napi_vector *nv, int napi_budget,
|
||||
struct fbnic_q_triad *qt, s32 head0)
|
||||
struct fbnic_q_triad *qt, s32 ts_head, s32 head0)
|
||||
{
|
||||
if (head0 >= 0)
|
||||
fbnic_clean_twq0(nv, napi_budget, &qt->sub0, false, head0);
|
||||
else if (ts_head >= 0)
|
||||
fbnic_clean_twq0(nv, napi_budget, &qt->sub0, false, ts_head);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -428,9 +557,9 @@ fbnic_clean_tcq(struct fbnic_napi_vector *nv, struct fbnic_q_triad *qt,
|
||||
int napi_budget)
|
||||
{
|
||||
struct fbnic_ring *cmpl = &qt->cmpl;
|
||||
s32 head0 = -1, ts_head = -1;
|
||||
__le64 *raw_tcd, done;
|
||||
u32 head = cmpl->head;
|
||||
s32 head0 = -1;
|
||||
|
||||
done = (head & (cmpl->size_mask + 1)) ? 0 : cpu_to_le64(FBNIC_TCD_DONE);
|
||||
raw_tcd = &cmpl->desc[head & cmpl->size_mask];
|
||||
@@ -453,6 +582,12 @@ fbnic_clean_tcq(struct fbnic_napi_vector *nv, struct fbnic_q_triad *qt,
|
||||
* they are skipped for now.
|
||||
*/
|
||||
break;
|
||||
case FBNIC_TCD_TYPE_1:
|
||||
if (WARN_ON_ONCE(tcd & FBNIC_TCD_TWQ1))
|
||||
break;
|
||||
|
||||
fbnic_clean_tsq(nv, &qt->sub0, tcd, &ts_head, &head0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -472,7 +607,7 @@ fbnic_clean_tcq(struct fbnic_napi_vector *nv, struct fbnic_q_triad *qt,
|
||||
}
|
||||
|
||||
/* Unmap and free processed buffers */
|
||||
fbnic_clean_twq(nv, napi_budget, qt, head0);
|
||||
fbnic_clean_twq(nv, napi_budget, qt, ts_head, head0);
|
||||
}
|
||||
|
||||
static void fbnic_clean_bdq(struct fbnic_napi_vector *nv, int napi_budget,
|
||||
@@ -707,6 +842,10 @@ static struct sk_buff *fbnic_build_skb(struct fbnic_napi_vector *nv,
|
||||
/* Set MAC header specific fields */
|
||||
skb->protocol = eth_type_trans(skb, nv->napi.dev);
|
||||
|
||||
/* Add timestamp if present */
|
||||
if (pkt->hwtstamp)
|
||||
skb_hwtstamps(skb)->hwtstamp = pkt->hwtstamp;
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
@@ -717,6 +856,23 @@ static enum pkt_hash_types fbnic_skb_hash_type(u64 rcd)
|
||||
PKT_HASH_TYPE_L2;
|
||||
}
|
||||
|
||||
static void fbnic_rx_tstamp(struct fbnic_napi_vector *nv, u64 rcd,
|
||||
struct fbnic_pkt_buff *pkt)
|
||||
{
|
||||
struct fbnic_net *fbn;
|
||||
u64 ns, ts;
|
||||
|
||||
if (!FIELD_GET(FBNIC_RCD_OPT_META_TS, rcd))
|
||||
return;
|
||||
|
||||
fbn = netdev_priv(nv->napi.dev);
|
||||
ts = FIELD_GET(FBNIC_RCD_OPT_META_TS_MASK, rcd);
|
||||
ns = fbnic_ts40_to_ns(fbn, ts);
|
||||
|
||||
/* Add timestamp to shared info */
|
||||
pkt->hwtstamp = ns_to_ktime(ns);
|
||||
}
|
||||
|
||||
static void fbnic_populate_skb_fields(struct fbnic_napi_vector *nv,
|
||||
u64 rcd, struct sk_buff *skb,
|
||||
struct fbnic_q_triad *qt)
|
||||
@@ -781,6 +937,8 @@ static int fbnic_clean_rcq(struct fbnic_napi_vector *nv,
|
||||
if (FIELD_GET(FBNIC_RCD_OPT_META_TYPE_MASK, rcd))
|
||||
break;
|
||||
|
||||
fbnic_rx_tstamp(nv, rcd, pkt);
|
||||
|
||||
/* We currently ignore the action table index */
|
||||
break;
|
||||
case FBNIC_RCD_TYPE_META:
|
||||
@@ -907,6 +1065,8 @@ static void fbnic_aggregate_ring_tx_counters(struct fbnic_net *fbn,
|
||||
fbn->tx_stats.bytes += stats->bytes;
|
||||
fbn->tx_stats.packets += stats->packets;
|
||||
fbn->tx_stats.dropped += stats->dropped;
|
||||
fbn->tx_stats.ts_lost += stats->ts_lost;
|
||||
fbn->tx_stats.ts_packets += stats->ts_packets;
|
||||
}
|
||||
|
||||
static void fbnic_remove_tx_ring(struct fbnic_net *fbn,
|
||||
|
||||
@@ -47,6 +47,7 @@ struct fbnic_net;
|
||||
|
||||
struct fbnic_pkt_buff {
|
||||
struct xdp_buff buff;
|
||||
ktime_t hwtstamp;
|
||||
u32 data_truesize;
|
||||
u16 data_len;
|
||||
u16 nr_frags;
|
||||
@@ -56,6 +57,8 @@ struct fbnic_queue_stats {
|
||||
u64 packets;
|
||||
u64 bytes;
|
||||
u64 dropped;
|
||||
u64 ts_packets;
|
||||
u64 ts_lost;
|
||||
struct u64_stats_sync syncp;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user