Skip to content

Commit

Permalink
Merge branch 'fix-broken-ptp-over-ip-on-ocelot-switches'
Browse files Browse the repository at this point in the history
Vladimir Oltean says:

====================
Fix broken PTP over IP on Ocelot switches

Changes in v2: added patch 5, added Richard's ack for the whole series
sans patch 5 which is new.

Po Liu reported recently that timestamping PTP over IPv4 is broken using
the felix driver on NXP LS1028A. This has been known for a while, of
course, since it has always been broken. The reason is because IP PTP
packets are currently treated as unknown IP multicast, which is not
flooded to the CPU port in the ocelot driver design, so packets don't
reach the ptp4l program.

The series solves the problem by installing packet traps per port when
the timestamping ioctl is called, depending on the RX filter selected
(L2, L4 or both).
====================

Link: https://lore.kernel.org/r/20211126172845.3149260-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Nov 26, 2021
2 parents b32e521 + c49a35e commit 32c5449
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 8 deletions.
252 changes: 244 additions & 8 deletions drivers/net/ethernet/mscc/ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,225 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port,
}
EXPORT_SYMBOL(ocelot_fdb_dump);

static void ocelot_populate_l2_ptp_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_ETYPE;
*(__be16 *)trap->key.etype.etype.value = htons(ETH_P_1588);
*(__be16 *)trap->key.etype.etype.mask = htons(0xffff);
}

static void
ocelot_populate_ipv4_ptp_event_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV4;
trap->key.ipv4.dport.value = PTP_EV_PORT;
trap->key.ipv4.dport.mask = 0xffff;
}

static void
ocelot_populate_ipv6_ptp_event_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV6;
trap->key.ipv6.dport.value = PTP_EV_PORT;
trap->key.ipv6.dport.mask = 0xffff;
}

static void
ocelot_populate_ipv4_ptp_general_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV4;
trap->key.ipv4.dport.value = PTP_GEN_PORT;
trap->key.ipv4.dport.mask = 0xffff;
}

static void
ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV6;
trap->key.ipv6.dport.value = PTP_GEN_PORT;
trap->key.ipv6.dport.mask = 0xffff;
}

static int ocelot_trap_add(struct ocelot *ocelot, int port,
unsigned long cookie,
void (*populate)(struct ocelot_vcap_filter *f))
{
struct ocelot_vcap_block *block_vcap_is2;
struct ocelot_vcap_filter *trap;
bool new = false;
int err;

block_vcap_is2 = &ocelot->block[VCAP_IS2];

trap = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, cookie,
false);
if (!trap) {
trap = kzalloc(sizeof(*trap), GFP_KERNEL);
if (!trap)
return -ENOMEM;

populate(trap);
trap->prio = 1;
trap->id.cookie = cookie;
trap->id.tc_offload = false;
trap->block_id = VCAP_IS2;
trap->type = OCELOT_VCAP_FILTER_OFFLOAD;
trap->lookup = 0;
trap->action.cpu_copy_ena = true;
trap->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
trap->action.port_mask = 0;
new = true;
}

trap->ingress_port_mask |= BIT(port);

if (new)
err = ocelot_vcap_filter_add(ocelot, trap, NULL);
else
err = ocelot_vcap_filter_replace(ocelot, trap);
if (err) {
trap->ingress_port_mask &= ~BIT(port);
if (!trap->ingress_port_mask)
kfree(trap);
return err;
}

return 0;
}

static int ocelot_trap_del(struct ocelot *ocelot, int port,
unsigned long cookie)
{
struct ocelot_vcap_block *block_vcap_is2;
struct ocelot_vcap_filter *trap;

block_vcap_is2 = &ocelot->block[VCAP_IS2];

trap = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, cookie,
false);
if (!trap)
return 0;

trap->ingress_port_mask &= ~BIT(port);
if (!trap->ingress_port_mask)
return ocelot_vcap_filter_del(ocelot, trap);

return ocelot_vcap_filter_replace(ocelot, trap);
}

static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long l2_cookie = ocelot->num_phys_ports + 1;

return ocelot_trap_add(ocelot, port, l2_cookie,
ocelot_populate_l2_ptp_trap_key);
}

static int ocelot_l2_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long l2_cookie = ocelot->num_phys_ports + 1;

return ocelot_trap_del(ocelot, port, l2_cookie);
}

static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long ipv4_gen_cookie = ocelot->num_phys_ports + 2;
unsigned long ipv4_ev_cookie = ocelot->num_phys_ports + 3;
int err;

err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie,
ocelot_populate_ipv4_ptp_event_trap_key);
if (err)
return err;

err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie,
ocelot_populate_ipv4_ptp_general_trap_key);
if (err)
ocelot_trap_del(ocelot, port, ipv4_ev_cookie);

return err;
}

static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long ipv4_gen_cookie = ocelot->num_phys_ports + 2;
unsigned long ipv4_ev_cookie = ocelot->num_phys_ports + 3;
int err;

err = ocelot_trap_del(ocelot, port, ipv4_ev_cookie);
err |= ocelot_trap_del(ocelot, port, ipv4_gen_cookie);
return err;
}

static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long ipv6_gen_cookie = ocelot->num_phys_ports + 4;
unsigned long ipv6_ev_cookie = ocelot->num_phys_ports + 5;
int err;

err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie,
ocelot_populate_ipv6_ptp_event_trap_key);
if (err)
return err;

err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie,
ocelot_populate_ipv6_ptp_general_trap_key);
if (err)
ocelot_trap_del(ocelot, port, ipv6_ev_cookie);

return err;
}

static int ocelot_ipv6_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long ipv6_gen_cookie = ocelot->num_phys_ports + 4;
unsigned long ipv6_ev_cookie = ocelot->num_phys_ports + 5;
int err;

err = ocelot_trap_del(ocelot, port, ipv6_ev_cookie);
err |= ocelot_trap_del(ocelot, port, ipv6_gen_cookie);
return err;
}

static int ocelot_setup_ptp_traps(struct ocelot *ocelot, int port,
bool l2, bool l4)
{
int err;

if (l2)
err = ocelot_l2_ptp_trap_add(ocelot, port);
else
err = ocelot_l2_ptp_trap_del(ocelot, port);
if (err)
return err;

if (l4) {
err = ocelot_ipv4_ptp_trap_add(ocelot, port);
if (err)
goto err_ipv4;

err = ocelot_ipv6_ptp_trap_add(ocelot, port);
if (err)
goto err_ipv6;
} else {
err = ocelot_ipv4_ptp_trap_del(ocelot, port);

err |= ocelot_ipv6_ptp_trap_del(ocelot, port);
}
if (err)
return err;

return 0;

err_ipv6:
ocelot_ipv4_ptp_trap_del(ocelot, port);
err_ipv4:
if (l2)
ocelot_l2_ptp_trap_del(ocelot, port);
return err;
}

int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
{
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
Expand All @@ -1288,7 +1507,9 @@ EXPORT_SYMBOL(ocelot_hwstamp_get);
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
bool l2 = false, l4 = false;
struct hwtstamp_config cfg;
int err;

if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
Expand Down Expand Up @@ -1320,28 +1541,40 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
switch (cfg.rx_filter) {
case HWTSTAMP_FILTER_NONE:
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_NTP_ALL:
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
l4 = true;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
l2 = true;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
l2 = true;
l4 = true;
break;
default:
mutex_unlock(&ocelot->ptp_lock);
return -ERANGE;
}

err = ocelot_setup_ptp_traps(ocelot, port, l2, l4);
if (err)
return err;

if (l2 && l4)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
else if (l2)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
else if (l4)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
else
cfg.rx_filter = HWTSTAMP_FILTER_NONE;

/* Commit back the result & save it */
memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg));
mutex_unlock(&ocelot->ptp_lock);
Expand Down Expand Up @@ -1444,7 +1677,10 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
SOF_TIMESTAMPING_RAW_HARDWARE;
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
BIT(HWTSTAMP_TX_ONESTEP_SYNC);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT);

return 0;
}
Expand Down
16 changes: 16 additions & 0 deletions drivers/net/ethernet/mscc/ocelot_vcap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,22 @@ int ocelot_vcap_filter_del(struct ocelot *ocelot,
}
EXPORT_SYMBOL(ocelot_vcap_filter_del);

int ocelot_vcap_filter_replace(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter)
{
struct ocelot_vcap_block *block = &ocelot->block[filter->block_id];
int index;

index = ocelot_vcap_block_get_filter_index(block, filter);
if (index < 0)
return index;

vcap_entry_set(ocelot, index, filter);

return 0;
}
EXPORT_SYMBOL(ocelot_vcap_filter_replace);

int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter)
{
Expand Down
1 change: 1 addition & 0 deletions include/linux/ptp_classify.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#define PTP_MSGTYPE_PDELAY_RESP 0x3

#define PTP_EV_PORT 319
#define PTP_GEN_PORT 320
#define PTP_GEN_BIT 0x08 /* indicates general message, if set in message type */

#define OFF_PTP_SOURCE_UUID 22 /* PTPv1 only */
Expand Down
2 changes: 2 additions & 0 deletions include/soc/mscc/ocelot_vcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,8 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot,
struct netlink_ext_ack *extack);
int ocelot_vcap_filter_del(struct ocelot *ocelot,
struct ocelot_vcap_filter *rule);
int ocelot_vcap_filter_replace(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter);
struct ocelot_vcap_filter *
ocelot_vcap_block_find_filter_by_id(struct ocelot_vcap_block *block,
unsigned long cookie, bool tc_offload);
Expand Down

0 comments on commit 32c5449

Please sign in to comment.