|
@@ -20,11 +20,13 @@
|
|
#include <linux/bpf.h>
|
|
#include <linux/bpf.h>
|
|
#include <linux/bpf_trace.h>
|
|
#include <linux/bpf_trace.h>
|
|
#include <linux/filter.h>
|
|
#include <linux/filter.h>
|
|
|
|
+#include <linux/net_tstamp.h>
|
|
|
|
|
|
#include "nic_reg.h"
|
|
#include "nic_reg.h"
|
|
#include "nic.h"
|
|
#include "nic.h"
|
|
#include "nicvf_queues.h"
|
|
#include "nicvf_queues.h"
|
|
#include "thunder_bgx.h"
|
|
#include "thunder_bgx.h"
|
|
|
|
+#include "../common/cavium_ptp.h"
|
|
|
|
|
|
#define DRV_NAME "thunder-nicvf"
|
|
#define DRV_NAME "thunder-nicvf"
|
|
#define DRV_VERSION "1.0"
|
|
#define DRV_VERSION "1.0"
|
|
@@ -602,6 +604,44 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog,
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void nicvf_snd_ptp_handler(struct net_device *netdev,
|
|
|
|
+ struct cqe_send_t *cqe_tx)
|
|
|
|
+{
|
|
|
|
+ struct nicvf *nic = netdev_priv(netdev);
|
|
|
|
+ struct skb_shared_hwtstamps ts;
|
|
|
|
+ u64 ns;
|
|
|
|
+
|
|
|
|
+ nic = nic->pnicvf;
|
|
|
|
+
|
|
|
|
+ /* Sync for 'ptp_skb' */
|
|
|
|
+ smp_rmb();
|
|
|
|
+
|
|
|
|
+ /* New timestamp request can be queued now */
|
|
|
|
+ atomic_set(&nic->tx_ptp_skbs, 0);
|
|
|
|
+
|
|
|
|
+ /* Check for timestamp requested skb */
|
|
|
|
+ if (!nic->ptp_skb)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* Check if timestamping is timedout, which is set to 10us */
|
|
|
|
+ if (cqe_tx->send_status == CQ_TX_ERROP_TSTMP_TIMEOUT ||
|
|
|
|
+ cqe_tx->send_status == CQ_TX_ERROP_TSTMP_CONFLICT)
|
|
|
|
+ goto no_tstamp;
|
|
|
|
+
|
|
|
|
+ /* Get the timestamp */
|
|
|
|
+ memset(&ts, 0, sizeof(ts));
|
|
|
|
+ ns = cavium_ptp_tstamp2time(nic->ptp_clock, cqe_tx->ptp_timestamp);
|
|
|
|
+ ts.hwtstamp = ns_to_ktime(ns);
|
|
|
|
+ skb_tstamp_tx(nic->ptp_skb, &ts);
|
|
|
|
+
|
|
|
|
+no_tstamp:
|
|
|
|
+ /* Free the original skb */
|
|
|
|
+ dev_kfree_skb_any(nic->ptp_skb);
|
|
|
|
+ nic->ptp_skb = NULL;
|
|
|
|
+ /* Sync 'ptp_skb' */
|
|
|
|
+ smp_wmb();
|
|
|
|
+}
|
|
|
|
+
|
|
static void nicvf_snd_pkt_handler(struct net_device *netdev,
|
|
static void nicvf_snd_pkt_handler(struct net_device *netdev,
|
|
struct cqe_send_t *cqe_tx,
|
|
struct cqe_send_t *cqe_tx,
|
|
int budget, int *subdesc_cnt,
|
|
int budget, int *subdesc_cnt,
|
|
@@ -658,7 +698,12 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev,
|
|
prefetch(skb);
|
|
prefetch(skb);
|
|
(*tx_pkts)++;
|
|
(*tx_pkts)++;
|
|
*tx_bytes += skb->len;
|
|
*tx_bytes += skb->len;
|
|
- napi_consume_skb(skb, budget);
|
|
|
|
|
|
+ /* If timestamp is requested for this skb, don't free it */
|
|
|
|
+ if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS &&
|
|
|
|
+ !nic->pnicvf->ptp_skb)
|
|
|
|
+ nic->pnicvf->ptp_skb = skb;
|
|
|
|
+ else
|
|
|
|
+ napi_consume_skb(skb, budget);
|
|
sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL;
|
|
sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL;
|
|
} else {
|
|
} else {
|
|
/* In case of SW TSO on 88xx, only last segment will have
|
|
/* In case of SW TSO on 88xx, only last segment will have
|
|
@@ -697,6 +742,21 @@ static inline void nicvf_set_rxhash(struct net_device *netdev,
|
|
skb_set_hash(skb, hash, hash_type);
|
|
skb_set_hash(skb, hash, hash_type);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline void nicvf_set_rxtstamp(struct nicvf *nic, struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ u64 ns;
|
|
|
|
+
|
|
|
|
+ if (!nic->ptp_clock || !nic->hw_rx_tstamp)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* The first 8 bytes is the timestamp */
|
|
|
|
+ ns = cavium_ptp_tstamp2time(nic->ptp_clock,
|
|
|
|
+ be64_to_cpu(*(__be64 *)skb->data));
|
|
|
|
+ skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(ns);
|
|
|
|
+
|
|
|
|
+ __skb_pull(skb, 8);
|
|
|
|
+}
|
|
|
|
+
|
|
static void nicvf_rcv_pkt_handler(struct net_device *netdev,
|
|
static void nicvf_rcv_pkt_handler(struct net_device *netdev,
|
|
struct napi_struct *napi,
|
|
struct napi_struct *napi,
|
|
struct cqe_rx_t *cqe_rx,
|
|
struct cqe_rx_t *cqe_rx,
|
|
@@ -748,6 +808,7 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev,
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ nicvf_set_rxtstamp(nic, skb);
|
|
nicvf_set_rxhash(netdev, cqe_rx, skb);
|
|
nicvf_set_rxhash(netdev, cqe_rx, skb);
|
|
|
|
|
|
skb_record_rx_queue(skb, rq_idx);
|
|
skb_record_rx_queue(skb, rq_idx);
|
|
@@ -823,10 +884,12 @@ loop:
|
|
&tx_pkts, &tx_bytes);
|
|
&tx_pkts, &tx_bytes);
|
|
tx_done++;
|
|
tx_done++;
|
|
break;
|
|
break;
|
|
|
|
+ case CQE_TYPE_SEND_PTP:
|
|
|
|
+ nicvf_snd_ptp_handler(netdev, (void *)cq_desc);
|
|
|
|
+ break;
|
|
case CQE_TYPE_INVALID:
|
|
case CQE_TYPE_INVALID:
|
|
case CQE_TYPE_RX_SPLIT:
|
|
case CQE_TYPE_RX_SPLIT:
|
|
case CQE_TYPE_RX_TCP:
|
|
case CQE_TYPE_RX_TCP:
|
|
- case CQE_TYPE_SEND_PTP:
|
|
|
|
/* Ignore for now */
|
|
/* Ignore for now */
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -1322,12 +1385,28 @@ int nicvf_stop(struct net_device *netdev)
|
|
|
|
|
|
nicvf_free_cq_poll(nic);
|
|
nicvf_free_cq_poll(nic);
|
|
|
|
|
|
|
|
+ /* Free any pending SKB saved to receive timestamp */
|
|
|
|
+ if (nic->ptp_skb) {
|
|
|
|
+ dev_kfree_skb_any(nic->ptp_skb);
|
|
|
|
+ nic->ptp_skb = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Clear multiqset info */
|
|
/* Clear multiqset info */
|
|
nic->pnicvf = nic;
|
|
nic->pnicvf = nic;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nicvf_config_hw_rx_tstamp(struct nicvf *nic, bool enable)
|
|
|
|
+{
|
|
|
|
+ union nic_mbx mbx = {};
|
|
|
|
+
|
|
|
|
+ mbx.ptp.msg = NIC_MBOX_MSG_PTP_CFG;
|
|
|
|
+ mbx.ptp.enable = enable;
|
|
|
|
+
|
|
|
|
+ return nicvf_send_msg_to_pf(nic, &mbx);
|
|
|
|
+}
|
|
|
|
+
|
|
static int nicvf_update_hw_max_frs(struct nicvf *nic, int mtu)
|
|
static int nicvf_update_hw_max_frs(struct nicvf *nic, int mtu)
|
|
{
|
|
{
|
|
union nic_mbx mbx = {};
|
|
union nic_mbx mbx = {};
|
|
@@ -1397,6 +1476,12 @@ int nicvf_open(struct net_device *netdev)
|
|
if (nic->sqs_mode)
|
|
if (nic->sqs_mode)
|
|
nicvf_get_primary_vf_struct(nic);
|
|
nicvf_get_primary_vf_struct(nic);
|
|
|
|
|
|
|
|
+ /* Configure PTP timestamp */
|
|
|
|
+ if (nic->ptp_clock)
|
|
|
|
+ nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp);
|
|
|
|
+ atomic_set(&nic->tx_ptp_skbs, 0);
|
|
|
|
+ nic->ptp_skb = NULL;
|
|
|
|
+
|
|
/* Configure receive side scaling and MTU */
|
|
/* Configure receive side scaling and MTU */
|
|
if (!nic->sqs_mode) {
|
|
if (!nic->sqs_mode) {
|
|
nicvf_rss_init(nic);
|
|
nicvf_rss_init(nic);
|
|
@@ -1823,6 +1908,73 @@ static void nicvf_xdp_flush(struct net_device *dev)
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
|
|
|
|
+{
|
|
|
|
+ struct hwtstamp_config config;
|
|
|
|
+ struct nicvf *nic = netdev_priv(netdev);
|
|
|
|
+
|
|
|
|
+ if (!nic->ptp_clock)
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ /* reserved for future extensions */
|
|
|
|
+ if (config.flags)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ switch (config.tx_type) {
|
|
|
|
+ case HWTSTAMP_TX_OFF:
|
|
|
|
+ case HWTSTAMP_TX_ON:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -ERANGE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (config.rx_filter) {
|
|
|
|
+ case HWTSTAMP_FILTER_NONE:
|
|
|
|
+ nic->hw_rx_tstamp = false;
|
|
|
|
+ break;
|
|
|
|
+ case HWTSTAMP_FILTER_ALL:
|
|
|
|
+ case HWTSTAMP_FILTER_SOME:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
|
|
|
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
|
|
|
+ nic->hw_rx_tstamp = true;
|
|
|
|
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -ERANGE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (netif_running(netdev))
|
|
|
|
+ nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp);
|
|
|
|
+
|
|
|
|
+ if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
|
|
|
|
+{
|
|
|
|
+ switch (cmd) {
|
|
|
|
+ case SIOCSHWTSTAMP:
|
|
|
|
+ return nicvf_config_hwtstamp(netdev, req);
|
|
|
|
+ default:
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static const struct net_device_ops nicvf_netdev_ops = {
|
|
static const struct net_device_ops nicvf_netdev_ops = {
|
|
.ndo_open = nicvf_open,
|
|
.ndo_open = nicvf_open,
|
|
.ndo_stop = nicvf_stop,
|
|
.ndo_stop = nicvf_stop,
|
|
@@ -1836,6 +1988,7 @@ static const struct net_device_ops nicvf_netdev_ops = {
|
|
.ndo_bpf = nicvf_xdp,
|
|
.ndo_bpf = nicvf_xdp,
|
|
.ndo_xdp_xmit = nicvf_xdp_xmit,
|
|
.ndo_xdp_xmit = nicvf_xdp_xmit,
|
|
.ndo_xdp_flush = nicvf_xdp_flush,
|
|
.ndo_xdp_flush = nicvf_xdp_flush,
|
|
|
|
+ .ndo_do_ioctl = nicvf_ioctl,
|
|
};
|
|
};
|
|
|
|
|
|
static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
@@ -1845,6 +1998,16 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
struct nicvf *nic;
|
|
struct nicvf *nic;
|
|
int err, qcount;
|
|
int err, qcount;
|
|
u16 sdevid;
|
|
u16 sdevid;
|
|
|
|
+ struct cavium_ptp *ptp_clock;
|
|
|
|
+
|
|
|
|
+ ptp_clock = cavium_ptp_get();
|
|
|
|
+ if (IS_ERR(ptp_clock)) {
|
|
|
|
+ if (PTR_ERR(ptp_clock) == -ENODEV)
|
|
|
|
+ /* In virtualized environment we proceed without ptp */
|
|
|
|
+ ptp_clock = NULL;
|
|
|
|
+ else
|
|
|
|
+ return PTR_ERR(ptp_clock);
|
|
|
|
+ }
|
|
|
|
|
|
err = pci_enable_device(pdev);
|
|
err = pci_enable_device(pdev);
|
|
if (err) {
|
|
if (err) {
|
|
@@ -1899,6 +2062,7 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
*/
|
|
*/
|
|
if (!nic->t88)
|
|
if (!nic->t88)
|
|
nic->max_queues *= 2;
|
|
nic->max_queues *= 2;
|
|
|
|
+ nic->ptp_clock = ptp_clock;
|
|
|
|
|
|
/* MAP VF's configuration registers */
|
|
/* MAP VF's configuration registers */
|
|
nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
|
|
nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
|
|
@@ -2012,6 +2176,7 @@ static void nicvf_remove(struct pci_dev *pdev)
|
|
pci_set_drvdata(pdev, NULL);
|
|
pci_set_drvdata(pdev, NULL);
|
|
if (nic->drv_stats)
|
|
if (nic->drv_stats)
|
|
free_percpu(nic->drv_stats);
|
|
free_percpu(nic->drv_stats);
|
|
|
|
+ cavium_ptp_put(nic->ptp_clock);
|
|
free_netdev(netdev);
|
|
free_netdev(netdev);
|
|
pci_release_regions(pdev);
|
|
pci_release_regions(pdev);
|
|
pci_disable_device(pdev);
|
|
pci_disable_device(pdev);
|