Subject: Re: [PATCH v3 net-next 3/3] octeontx2-pf: Add support for PTP clock

From: Richard Cochran <[email-masked]>
Date: Thu, 9 Jul 2020 09:01:56 -0700

On Thu, Jul 09, 2020 at 06:57:01PM +0530, [email-masked] wrote:

> @@ -1736,6 +1751,143 @@ static void otx2_reset_task(struct work_struct *work)
>  	netif_trans_update(pf->netdev);
>  }
>  
> +static int otx2_config_hw_rx_tstamp(struct otx2_nic *pfvf, bool enable)
> +{
> +	struct msg_req *req;
> +	int err;
> +
> +	if (pfvf->flags & OTX2_FLAG_RX_TSTAMP_ENABLED && enable)
> +		return 0;

It appears that nothing protects pfvf->flags from concurrent access.
Please double check and correct if needed.

> +	mutex_lock(&pfvf->mbox.lock);
> +	if (enable)
> +		req = otx2_mbox_alloc_msg_cgx_ptp_rx_enable(&pfvf->mbox);
> +	else
> +		req = otx2_mbox_alloc_msg_cgx_ptp_rx_disable(&pfvf->mbox);
> +	if (!req) {
> +		mutex_unlock(&pfvf->mbox.lock);
> +		return -ENOMEM;
> +	}
> +
> +	err = otx2_sync_mbox_msg(&pfvf->mbox);
> +	if (err) {
> +		mutex_unlock(&pfvf->mbox.lock);
> +		return err;
> +	}
> +
> +	mutex_unlock(&pfvf->mbox.lock);
> +	if (enable)
> +		pfvf->flags |= OTX2_FLAG_RX_TSTAMP_ENABLED;
> +	else
> +		pfvf->flags &= ~OTX2_FLAG_RX_TSTAMP_ENABLED;
> +	return 0;
> +}
> +
> +static int otx2_config_hw_tx_tstamp(struct otx2_nic *pfvf, bool enable)
> +{
> +	struct msg_req *req;
> +	int err;
> +
> +	if (pfvf->flags & OTX2_FLAG_TX_TSTAMP_ENABLED && enable)
> +		return 0;

Again, please check concurrency here.

> +
> +	mutex_lock(&pfvf->mbox.lock);
> +	if (enable)
> +		req = otx2_mbox_alloc_msg_nix_lf_ptp_tx_enable(&pfvf->mbox);
> +	else
> +		req = otx2_mbox_alloc_msg_nix_lf_ptp_tx_disable(&pfvf->mbox);
> +	if (!req) {
> +		mutex_unlock(&pfvf->mbox.lock);
> +		return -ENOMEM;
> +	}
> +
> +	err = otx2_sync_mbox_msg(&pfvf->mbox);
> +	if (err) {
> +		mutex_unlock(&pfvf->mbox.lock);
> +		return err;
> +	}
> +
> +	mutex_unlock(&pfvf->mbox.lock);
> +	if (enable)
> +		pfvf->flags |= OTX2_FLAG_TX_TSTAMP_ENABLED;
> +	else
> +		pfvf->flags &= ~OTX2_FLAG_TX_TSTAMP_ENABLED;
> +	return 0;
> +}
> +
> +static int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
> +{
> +	struct otx2_nic *pfvf = netdev_priv(netdev);
> +	struct hwtstamp_config config;
> +
> +	if (!pfvf->ptp)
> +		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:
> +		otx2_config_hw_tx_tstamp(pfvf, false);
> +		break;
> +	case HWTSTAMP_TX_ON:
> +		otx2_config_hw_tx_tstamp(pfvf, true);
> +		break;
> +	default:
> +		return -ERANGE;
> +	}
> +
> +	switch (config.rx_filter) {
> +	case HWTSTAMP_FILTER_NONE:
> +		otx2_config_hw_rx_tstamp(pfvf, 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:
> +		otx2_config_hw_rx_tstamp(pfvf, true);
> +		config.rx_filter = HWTSTAMP_FILTER_ALL;
> +		break;
> +	default:
> +		return -ERANGE;
> +	}
> +
> +	memcpy(&pfvf->tstamp, &config, sizeof(config));
> +
> +	return copy_to_user(ifr->ifr_data, &config,
> +			    sizeof(config)) ? -EFAULT : 0;
> +}
> +
> +static int otx2_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
> +{
> +	struct otx2_nic *pfvf = netdev_priv(netdev);
> +	struct hwtstamp_config *cfg = &pfvf->tstamp;
> +

Need to test phy_has_hwtstamp() here and pass ioctl to PHY if true.

> +	switch (cmd) {
> +	case SIOCSHWTSTAMP:
> +		return otx2_config_hwtstamp(netdev, req);
> +	case SIOCGHWTSTAMP:
> +		return copy_to_user(req->ifr_data, cfg,
> +				    sizeof(*cfg)) ? -EFAULT : 0;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
>  static const struct net_device_ops otx2_netdev_ops = {
>  	.ndo_open		= otx2_open,
>  	.ndo_stop		= otx2_stop,

> diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c
> new file mode 100644
> index 0000000..28058bd
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c
> @@ -0,0 +1,208 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Marvell OcteonTx2 PTP support for ethernet driver */
> +
> +#include "otx2_common.h"
> +#include "otx2_ptp.h"
> +
> +static int otx2_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
> +{
> +	struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
> +					    ptp_info);
> +	struct ptp_req *req;
> +	int err;
> +
> +	if (!ptp->nic)
> +		return -ENODEV;
> +
> +	req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
> +	if (!req)
> +		return -ENOMEM;
> +
> +	req->op = PTP_OP_ADJFINE;
> +	req->scaled_ppm = scaled_ppm;
> +
> +	err = otx2_sync_mbox_msg(&ptp->nic->mbox);
> +	if (err)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static u64 ptp_cc_read(const struct cyclecounter *cc)
> +{
> +	struct otx2_ptp *ptp = container_of(cc, struct otx2_ptp, cycle_counter);
> +	struct ptp_req *req;
> +	struct ptp_rsp *rsp;
> +	int err;
> +
> +	if (!ptp->nic)
> +		return 0;
> +
> +	req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
> +	if (!req)
> +		return 0;
> +
> +	req->op = PTP_OP_GET_CLOCK;
> +
> +	err = otx2_sync_mbox_msg(&ptp->nic->mbox);
> +	if (err)
> +		return 0;
> +
> +	rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(&ptp->nic->mbox.mbox, 0,
> +						  &req->hdr);
> +	if (IS_ERR(rsp))
> +		return 0;
> +
> +	return rsp->clk;
> +}
> +
> +static int otx2_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
> +{
> +	struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
> +					    ptp_info);
> +	struct otx2_nic *pfvf = ptp->nic;
> +
> +	mutex_lock(&pfvf->mbox.lock);
> +	timecounter_adjtime(&ptp->time_counter, delta);
> +	mutex_unlock(&pfvf->mbox.lock);
> +
> +	return 0;
> +}
> +
> +static int otx2_ptp_gettime(struct ptp_clock_info *ptp_info,
> +			    struct timespec64 *ts)
> +{
> +	struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
> +					    ptp_info);
> +	struct otx2_nic *pfvf = ptp->nic;
> +	u64 nsec;
> +
> +	mutex_lock(&pfvf->mbox.lock);
> +	nsec = timecounter_read(&ptp->time_counter);
> +	mutex_unlock(&pfvf->mbox.lock);
> +
> +	*ts = ns_to_timespec64(nsec);
> +
> +	return 0;
> +}
> +
> +static int otx2_ptp_settime(struct ptp_clock_info *ptp_info,
> +			    const struct timespec64 *ts)
> +{
> +	struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
> +					    ptp_info);
> +	struct otx2_nic *pfvf = ptp->nic;
> +	u64 nsec;
> +
> +	nsec = timespec64_to_ns(ts);
> +
> +	mutex_lock(&pfvf->mbox.lock);
> +	timecounter_init(&ptp->time_counter, &ptp->cycle_counter, nsec);
> +	mutex_unlock(&pfvf->mbox.lock);
> +
> +	return 0;
> +}
> +
> +static int otx2_ptp_enable(struct ptp_clock_info *ptp_info,
> +			   struct ptp_clock_request *rq, int on)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +int otx2_ptp_init(struct otx2_nic *pfvf)
> +{
> +	struct otx2_ptp *ptp_ptr;
> +	struct cyclecounter *cc;
> +	struct ptp_req *req;
> +	int err;
> +
> +	mutex_lock(&pfvf->mbox.lock);
> +	/* check if PTP block is available */
> +	req = otx2_mbox_alloc_msg_ptp_op(&pfvf->mbox);
> +	if (!req) {
> +		mutex_unlock(&pfvf->mbox.lock);
> +		return -ENOMEM;
> +	}
> +
> +	req->op = PTP_OP_GET_CLOCK;
> +
> +	err = otx2_sync_mbox_msg(&pfvf->mbox);
> +	if (err) {
> +		mutex_unlock(&pfvf->mbox.lock);
> +		return err;
> +	}
> +	mutex_unlock(&pfvf->mbox.lock);
> +
> +	ptp_ptr = kzalloc(sizeof(*ptp_ptr), GFP_KERNEL);
> +	if (!ptp_ptr) {
> +		err = -ENOMEM;
> +		goto error;
> +	}
> +
> +	ptp_ptr->nic = pfvf;
> +
> +	cc = &ptp_ptr->cycle_counter;
> +	cc->read = ptp_cc_read;
> +	cc->mask = CYCLECOUNTER_MASK(64);
> +	cc->mult = 1;
> +	cc->shift = 0;
> +
> +	timecounter_init(&ptp_ptr->time_counter, &ptp_ptr->cycle_counter,
> +			 ktime_to_ns(ktime_get_real()));
> +
> +	ptp_ptr->ptp_info = (struct ptp_clock_info) {
> +		.owner          = THIS_MODULE,
> +		.name           = "OcteonTX2 PTP",
> +		.max_adj        = 1000000000ull,
> +		.n_ext_ts       = 0,
> +		.n_pins         = 0,
> +		.pps            = 0,
> +		.adjfine        = otx2_ptp_adjfine,
> +		.adjtime        = otx2_ptp_adjtime,
> +		.gettime64      = otx2_ptp_gettime,
> +		.settime64      = otx2_ptp_settime,
> +		.enable         = otx2_ptp_enable,
> +	};
> +
> +	ptp_ptr->ptp_clock = ptp_clock_register(&ptp_ptr->ptp_info, pfvf->dev);
> +	if (IS_ERR(ptp_ptr->ptp_clock)) {
> +		err = PTR_ERR(ptp_ptr->ptp_clock);
> +		kfree(ptp_ptr);
> +		goto error;
> +	}

You need to handle NULL here.

 * ptp_clock_register() - register a PTP hardware clock driver
 *
 * @info:   Structure describing the new clock.
 * @parent: Pointer to the parent device of the new clock.
 *
 * Returns a valid pointer on success or PTR_ERR on failure.  If PHC
 * support is missing at the configuration level, this function
 * returns NULL, and drivers are expected to gracefully handle that
 * case separately.

> +
> +	pfvf->ptp = ptp_ptr;
> +
> +error:
> +	return err;
> +}
> +
> +void otx2_ptp_destroy(struct otx2_nic *pfvf)
> +{
> +	struct otx2_ptp *ptp = pfvf->ptp;
> +
> +	if (!ptp)
> +		return;
> +
> +	ptp_clock_unregister(ptp->ptp_clock);
> +	kfree(ptp);
> +	pfvf->ptp = NULL;
> +}
> +
> +int otx2_ptp_clock_index(struct otx2_nic *pfvf)
> +{
> +	if (!pfvf->ptp)
> +		return -ENODEV;
> +
> +	return ptp_clock_index(pfvf->ptp->ptp_clock);
> +}
> +
> +int otx2_ptp_tstamp2time(struct otx2_nic *pfvf, u64 tstamp, u64 *tsns)
> +{
> +	if (!pfvf->ptp)
> +		return -ENODEV;
> +
> +	*tsns = timecounter_cyc2time(&pfvf->ptp->time_counter, tstamp);
> +
> +	return 0;
> +}

> diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c
> index 3a5b34a..1f90426 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c
> @@ -16,6 +16,7 @@
>  #include "otx2_common.h"
>  #include "otx2_struct.h"
>  #include "otx2_txrx.h"
> +#include "otx2_ptp.h"
>  
>  #define CQE_ADDR(CQ, idx) ((CQ)->cqe_base + ((CQ)->cqe_size * (idx)))
>  
> @@ -81,8 +82,11 @@ static void otx2_snd_pkt_handler(struct otx2_nic *pfvf,
>  				 int budget, int *tx_pkts, int *tx_bytes)
>  {
>  	struct nix_send_comp_s *snd_comp = &cqe->comp;
> +	struct skb_shared_hwtstamps ts;
>  	struct sk_buff *skb = NULL;
> +	u64 timestamp, tsns;
>  	struct sg_list *sg;
> +	int err;
>  
>  	if (unlikely(snd_comp->status) && netif_msg_tx_err(pfvf))
>  		net_err_ratelimited("%s: TX%d: Error in send CQ status:%x\n",
> @@ -94,6 +98,18 @@ static void otx2_snd_pkt_handler(struct otx2_nic *pfvf,
>  	if (unlikely(!skb))
>  		return;
>  
> +	if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) {

SKBTX_IN_PROGRESS may be set by the PHY, so you need to test whether
time stamping is enabled in your MAC driver as well.

> +		timestamp = ((u64 *)sq->timestamps->base)[snd_comp->sqe_id];
> +		if (timestamp != 1) {
> +			err = otx2_ptp_tstamp2time(pfvf, timestamp, &tsns);
> +			if (!err) {
> +				memset(&ts, 0, sizeof(ts));
> +				ts.hwtstamp = ns_to_ktime(tsns);
> +				skb_tstamp_tx(skb, &ts);
> +			}
> +		}
> +	}
> +
>  	*tx_bytes += skb->len;
>  	(*tx_pkts)++;
>  	otx2_dma_unmap_skb_frags(pfvf, sg);

Thanks,
Richard