Skip to content

Commit ab80e71

Browse files
committed
Merge branch 'ocelot-ptp-fixes'
Vladimir Oltean says: ==================== Ocelot PTP fixes This is another attempt at "net: mscc: ocelot: be resilient to loss of PTP packets during transmission". https://lore.kernel.org/netdev/20241203164755.16115-1-vladimir.oltean@nxp.com/ The central change is in patch 4/5. It recovers a port from a very reproducible condition where previously, it would become unable to take any hardware TX timestamp. Then we have patches 1/5 and 5/5 which are extra bug fixes. Patches 2/5 and 3/5 are logical changes split out of the monolithic patch previously submitted as v1, so that patch 4/5 is hopefully easier to follow. ==================== Link: https://patch.msgid.link/20241205145519.1236778-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents cdd0b91 + 43a4166 commit ab80e71

File tree

3 files changed

+130
-82
lines changed

3 files changed

+130
-82
lines changed

drivers/net/ethernet/mscc/ocelot_ptp.c

Lines changed: 129 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <soc/mscc/ocelot.h>
1515
#include "ocelot.h"
1616

17+
#define OCELOT_PTP_TX_TSTAMP_TIMEOUT (5 * HZ)
18+
1719
int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
1820
{
1921
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
@@ -495,6 +497,28 @@ static int ocelot_traps_to_ptp_rx_filter(unsigned int proto)
495497
return HWTSTAMP_FILTER_NONE;
496498
}
497499

500+
static int ocelot_ptp_tx_type_to_cmd(int tx_type, int *ptp_cmd)
501+
{
502+
switch (tx_type) {
503+
case HWTSTAMP_TX_ON:
504+
*ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
505+
break;
506+
case HWTSTAMP_TX_ONESTEP_SYNC:
507+
/* IFH_REW_OP_ONE_STEP_PTP updates the correctionField,
508+
* what we need to update is the originTimestamp.
509+
*/
510+
*ptp_cmd = IFH_REW_OP_ORIGIN_PTP;
511+
break;
512+
case HWTSTAMP_TX_OFF:
513+
*ptp_cmd = 0;
514+
break;
515+
default:
516+
return -ERANGE;
517+
}
518+
519+
return 0;
520+
}
521+
498522
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
499523
{
500524
struct ocelot_port *ocelot_port = ocelot->ports[port];
@@ -521,30 +545,19 @@ EXPORT_SYMBOL(ocelot_hwstamp_get);
521545
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
522546
{
523547
struct ocelot_port *ocelot_port = ocelot->ports[port];
548+
int ptp_cmd, old_ptp_cmd = ocelot_port->ptp_cmd;
524549
bool l2 = false, l4 = false;
525550
struct hwtstamp_config cfg;
551+
bool old_l2, old_l4;
526552
int err;
527553

528554
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
529555
return -EFAULT;
530556

531557
/* Tx type sanity check */
532-
switch (cfg.tx_type) {
533-
case HWTSTAMP_TX_ON:
534-
ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
535-
break;
536-
case HWTSTAMP_TX_ONESTEP_SYNC:
537-
/* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we
538-
* need to update the origin time.
539-
*/
540-
ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP;
541-
break;
542-
case HWTSTAMP_TX_OFF:
543-
ocelot_port->ptp_cmd = 0;
544-
break;
545-
default:
546-
return -ERANGE;
547-
}
558+
err = ocelot_ptp_tx_type_to_cmd(cfg.tx_type, &ptp_cmd);
559+
if (err)
560+
return err;
548561

549562
switch (cfg.rx_filter) {
550563
case HWTSTAMP_FILTER_NONE:
@@ -569,13 +582,27 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
569582
return -ERANGE;
570583
}
571584

585+
old_l2 = ocelot_port->trap_proto & OCELOT_PROTO_PTP_L2;
586+
old_l4 = ocelot_port->trap_proto & OCELOT_PROTO_PTP_L4;
587+
572588
err = ocelot_setup_ptp_traps(ocelot, port, l2, l4);
573589
if (err)
574590
return err;
575591

592+
ocelot_port->ptp_cmd = ptp_cmd;
593+
576594
cfg.rx_filter = ocelot_traps_to_ptp_rx_filter(ocelot_port->trap_proto);
577595

578-
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
596+
if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg))) {
597+
err = -EFAULT;
598+
goto out_restore_ptp_traps;
599+
}
600+
601+
return 0;
602+
out_restore_ptp_traps:
603+
ocelot_setup_ptp_traps(ocelot, port, old_l2, old_l4);
604+
ocelot_port->ptp_cmd = old_ptp_cmd;
605+
return err;
579606
}
580607
EXPORT_SYMBOL(ocelot_hwstamp_set);
581608

@@ -603,34 +630,87 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
603630
}
604631
EXPORT_SYMBOL(ocelot_get_ts_info);
605632

606-
static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
607-
struct sk_buff *clone)
633+
static struct sk_buff *ocelot_port_dequeue_ptp_tx_skb(struct ocelot *ocelot,
634+
int port, u8 ts_id,
635+
u32 seqid)
608636
{
609637
struct ocelot_port *ocelot_port = ocelot->ports[port];
610-
unsigned long flags;
638+
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
639+
struct ptp_header *hdr;
611640

612-
spin_lock_irqsave(&ocelot->ts_id_lock, flags);
641+
spin_lock(&ocelot->ts_id_lock);
613642

614-
if (ocelot_port->ptp_skbs_in_flight == OCELOT_MAX_PTP_ID ||
615-
ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) {
616-
spin_unlock_irqrestore(&ocelot->ts_id_lock, flags);
617-
return -EBUSY;
643+
skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) {
644+
if (OCELOT_SKB_CB(skb)->ts_id != ts_id)
645+
continue;
646+
647+
/* Check that the timestamp ID is for the expected PTP
648+
* sequenceId. We don't have to test ptp_parse_header() against
649+
* NULL, because we've pre-validated the packet's ptp_class.
650+
*/
651+
hdr = ptp_parse_header(skb, OCELOT_SKB_CB(skb)->ptp_class);
652+
if (seqid != ntohs(hdr->sequence_id))
653+
continue;
654+
655+
__skb_unlink(skb, &ocelot_port->tx_skbs);
656+
ocelot->ptp_skbs_in_flight--;
657+
skb_match = skb;
658+
break;
618659
}
619660

620-
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
621-
/* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */
622-
OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id;
661+
spin_unlock(&ocelot->ts_id_lock);
623662

624-
ocelot_port->ts_id++;
625-
if (ocelot_port->ts_id == OCELOT_MAX_PTP_ID)
626-
ocelot_port->ts_id = 0;
663+
return skb_match;
664+
}
665+
666+
static int ocelot_port_queue_ptp_tx_skb(struct ocelot *ocelot, int port,
667+
struct sk_buff *clone)
668+
{
669+
struct ocelot_port *ocelot_port = ocelot->ports[port];
670+
DECLARE_BITMAP(ts_id_in_flight, OCELOT_MAX_PTP_ID);
671+
struct sk_buff *skb, *skb_tmp;
672+
unsigned long n;
673+
674+
spin_lock(&ocelot->ts_id_lock);
675+
676+
/* To get a better chance of acquiring a timestamp ID, first flush the
677+
* stale packets still waiting in the TX timestamping queue. They are
678+
* probably lost.
679+
*/
680+
skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) {
681+
if (time_before(OCELOT_SKB_CB(skb)->ptp_tx_time +
682+
OCELOT_PTP_TX_TSTAMP_TIMEOUT, jiffies)) {
683+
dev_warn_ratelimited(ocelot->dev,
684+
"port %d invalidating stale timestamp ID %u which seems lost\n",
685+
port, OCELOT_SKB_CB(skb)->ts_id);
686+
__skb_unlink(skb, &ocelot_port->tx_skbs);
687+
kfree_skb(skb);
688+
ocelot->ptp_skbs_in_flight--;
689+
} else {
690+
__set_bit(OCELOT_SKB_CB(skb)->ts_id, ts_id_in_flight);
691+
}
692+
}
693+
694+
if (ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) {
695+
spin_unlock(&ocelot->ts_id_lock);
696+
return -EBUSY;
697+
}
698+
699+
n = find_first_zero_bit(ts_id_in_flight, OCELOT_MAX_PTP_ID);
700+
if (n == OCELOT_MAX_PTP_ID) {
701+
spin_unlock(&ocelot->ts_id_lock);
702+
return -EBUSY;
703+
}
627704

628-
ocelot_port->ptp_skbs_in_flight++;
705+
/* Found an available timestamp ID, use it */
706+
OCELOT_SKB_CB(clone)->ts_id = n;
707+
OCELOT_SKB_CB(clone)->ptp_tx_time = jiffies;
629708
ocelot->ptp_skbs_in_flight++;
709+
__skb_queue_tail(&ocelot_port->tx_skbs, clone);
630710

631-
skb_queue_tail(&ocelot_port->tx_skbs, clone);
711+
spin_unlock(&ocelot->ts_id_lock);
632712

633-
spin_unlock_irqrestore(&ocelot->ts_id_lock, flags);
713+
dev_dbg_ratelimited(ocelot->dev, "port %d timestamp id %lu\n", port, n);
634714

635715
return 0;
636716
}
@@ -687,10 +767,14 @@ int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
687767
if (!(*clone))
688768
return -ENOMEM;
689769

690-
err = ocelot_port_add_txtstamp_skb(ocelot, port, *clone);
691-
if (err)
770+
/* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */
771+
err = ocelot_port_queue_ptp_tx_skb(ocelot, port, *clone);
772+
if (err) {
773+
kfree_skb(*clone);
692774
return err;
775+
}
693776

777+
skb_shinfo(*clone)->tx_flags |= SKBTX_IN_PROGRESS;
694778
OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
695779
OCELOT_SKB_CB(*clone)->ptp_class = ptp_class;
696780
}
@@ -726,28 +810,15 @@ static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
726810
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
727811
}
728812

729-
static bool ocelot_validate_ptp_skb(struct sk_buff *clone, u16 seqid)
730-
{
731-
struct ptp_header *hdr;
732-
733-
hdr = ptp_parse_header(clone, OCELOT_SKB_CB(clone)->ptp_class);
734-
if (WARN_ON(!hdr))
735-
return false;
736-
737-
return seqid == ntohs(hdr->sequence_id);
738-
}
739-
740813
void ocelot_get_txtstamp(struct ocelot *ocelot)
741814
{
742815
int budget = OCELOT_PTP_QUEUE_SZ;
743816

744817
while (budget--) {
745-
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
746818
struct skb_shared_hwtstamps shhwtstamps;
747819
u32 val, id, seqid, txport;
748-
struct ocelot_port *port;
820+
struct sk_buff *skb_match;
749821
struct timespec64 ts;
750-
unsigned long flags;
751822

752823
val = ocelot_read(ocelot, SYS_PTP_STATUS);
753824

@@ -762,36 +833,14 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
762833
txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val);
763834
seqid = SYS_PTP_STATUS_PTP_MESS_SEQ_ID(val);
764835

765-
port = ocelot->ports[txport];
766-
767-
spin_lock(&ocelot->ts_id_lock);
768-
port->ptp_skbs_in_flight--;
769-
ocelot->ptp_skbs_in_flight--;
770-
spin_unlock(&ocelot->ts_id_lock);
771-
772836
/* Retrieve its associated skb */
773-
try_again:
774-
spin_lock_irqsave(&port->tx_skbs.lock, flags);
775-
776-
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
777-
if (OCELOT_SKB_CB(skb)->ts_id != id)
778-
continue;
779-
__skb_unlink(skb, &port->tx_skbs);
780-
skb_match = skb;
781-
break;
782-
}
783-
784-
spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
785-
786-
if (WARN_ON(!skb_match))
787-
continue;
788-
789-
if (!ocelot_validate_ptp_skb(skb_match, seqid)) {
790-
dev_err_ratelimited(ocelot->dev,
791-
"port %d received stale TX timestamp for seqid %d, discarding\n",
792-
txport, seqid);
793-
dev_kfree_skb_any(skb);
794-
goto try_again;
837+
skb_match = ocelot_port_dequeue_ptp_tx_skb(ocelot, txport, id,
838+
seqid);
839+
if (!skb_match) {
840+
dev_warn_ratelimited(ocelot->dev,
841+
"port %d received TX timestamp (seqid %d, ts id %u) for packet previously declared stale\n",
842+
txport, seqid, id);
843+
goto next_ts;
795844
}
796845

797846
/* Get the h/w timestamp */
@@ -802,7 +851,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
802851
shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
803852
skb_complete_tx_timestamp(skb_match, &shhwtstamps);
804853

805-
/* Next ts */
854+
next_ts:
806855
ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT);
807856
}
808857
}

include/linux/dsa/ocelot.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
struct ocelot_skb_cb {
1616
struct sk_buff *clone;
1717
unsigned int ptp_class; /* valid only for clones */
18+
unsigned long ptp_tx_time; /* valid only for clones */
1819
u32 tstamp_lo;
1920
u8 ptp_cmd;
2021
u8 ts_id;

include/soc/mscc/ocelot.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,15 +778,13 @@ struct ocelot_port {
778778

779779
phy_interface_t phy_mode;
780780

781-
unsigned int ptp_skbs_in_flight;
782781
struct sk_buff_head tx_skbs;
783782

784783
unsigned int trap_proto;
785784

786785
u16 mrp_ring_id;
787786

788787
u8 ptp_cmd;
789-
u8 ts_id;
790788

791789
u8 index;
792790

0 commit comments

Comments
 (0)