mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 08:31:55 +00:00
Merge branch 'fix-broken-ptp-over-ip-on-ocelot-switches'
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>
This commit is contained in:
commit
32c5449754
@ -1278,6 +1278,225 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ocelot_fdb_dump);
|
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)
|
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
|
||||||
{
|
{
|
||||||
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
|
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
|
||||||
@ -1288,7 +1507,9 @@ EXPORT_SYMBOL(ocelot_hwstamp_get);
|
|||||||
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
|
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
|
||||||
{
|
{
|
||||||
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
||||||
|
bool l2 = false, l4 = false;
|
||||||
struct hwtstamp_config cfg;
|
struct hwtstamp_config cfg;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@ -1320,28 +1541,40 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
|
|||||||
switch (cfg.rx_filter) {
|
switch (cfg.rx_filter) {
|
||||||
case HWTSTAMP_FILTER_NONE:
|
case HWTSTAMP_FILTER_NONE:
|
||||||
break;
|
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_EVENT:
|
||||||
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
||||||
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
||||||
|
l4 = true;
|
||||||
|
break;
|
||||||
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
||||||
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
||||||
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
||||||
|
l2 = true;
|
||||||
|
break;
|
||||||
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
||||||
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
||||||
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
||||||
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
l2 = true;
|
||||||
|
l4 = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mutex_unlock(&ocelot->ptp_lock);
|
mutex_unlock(&ocelot->ptp_lock);
|
||||||
return -ERANGE;
|
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 */
|
/* Commit back the result & save it */
|
||||||
memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg));
|
memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg));
|
||||||
mutex_unlock(&ocelot->ptp_lock);
|
mutex_unlock(&ocelot->ptp_lock);
|
||||||
@ -1444,7 +1677,10 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
|
|||||||
SOF_TIMESTAMPING_RAW_HARDWARE;
|
SOF_TIMESTAMPING_RAW_HARDWARE;
|
||||||
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
|
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
|
||||||
BIT(HWTSTAMP_TX_ONESTEP_SYNC);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1217,6 +1217,22 @@ int ocelot_vcap_filter_del(struct ocelot *ocelot,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ocelot_vcap_filter_del);
|
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,
|
int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
|
||||||
struct ocelot_vcap_filter *filter)
|
struct ocelot_vcap_filter *filter)
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#define PTP_MSGTYPE_PDELAY_RESP 0x3
|
#define PTP_MSGTYPE_PDELAY_RESP 0x3
|
||||||
|
|
||||||
#define PTP_EV_PORT 319
|
#define PTP_EV_PORT 319
|
||||||
|
#define PTP_GEN_PORT 320
|
||||||
#define PTP_GEN_BIT 0x08 /* indicates general message, if set in message type */
|
#define PTP_GEN_BIT 0x08 /* indicates general message, if set in message type */
|
||||||
|
|
||||||
#define OFF_PTP_SOURCE_UUID 22 /* PTPv1 only */
|
#define OFF_PTP_SOURCE_UUID 22 /* PTPv1 only */
|
||||||
|
@ -703,6 +703,8 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot,
|
|||||||
struct netlink_ext_ack *extack);
|
struct netlink_ext_ack *extack);
|
||||||
int ocelot_vcap_filter_del(struct ocelot *ocelot,
|
int ocelot_vcap_filter_del(struct ocelot *ocelot,
|
||||||
struct ocelot_vcap_filter *rule);
|
struct ocelot_vcap_filter *rule);
|
||||||
|
int ocelot_vcap_filter_replace(struct ocelot *ocelot,
|
||||||
|
struct ocelot_vcap_filter *filter);
|
||||||
struct ocelot_vcap_filter *
|
struct ocelot_vcap_filter *
|
||||||
ocelot_vcap_block_find_filter_by_id(struct ocelot_vcap_block *block,
|
ocelot_vcap_block_find_filter_by_id(struct ocelot_vcap_block *block,
|
||||||
unsigned long cookie, bool tc_offload);
|
unsigned long cookie, bool tc_offload);
|
||||||
|
Loading…
Reference in New Issue
Block a user