Merge branch 'sfc-ptp'

Edward Cree says:

====================
sfc: add support for PTP over IPv6 and 802.3
Most recent cards (8000 series and newer) had enough hardware support
for this, but it was not enabled in the driver. The transmission of PTP
packets over these protocols was already added in commit bd4a2697e5
("sfc: use hardware tx timestamps for more than PTP"), but receiving
them was already unsupported so synchronization didn't happen.

These patches add support for timestamping received packets over
IPv6/UPD and IEEE802.3.

v2: fixed weird indentation in efx_ptp_init_filter
v3: fixed bug caused by usage of htons in PTP_EVENT_PORT definition.
    It was used in more places, where htons was used too, so using it
    2 times leave it again in host order. I didn't detected it in my
    tests because it only affected if timestamping through the MC, but
    the model I used do it through the MAC. Detected by kernel test
    robot <lkp@intel.com>
v4: removed `inline` specifiers from 2 local functions
v5: restored deleted comment with useful explanation about packets
    reordering. Deleted useless whitespaces.
====================

Reviewed-by: Edward Cree <ecree.xilinx@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2022-09-07 12:23:25 +01:00
commit 98ba81081b
2 changed files with 114 additions and 42 deletions

View File

@ -9,6 +9,7 @@
#include <linux/types.h>
#include <linux/if_ether.h>
#include <linux/in6.h>
#include <asm/byteorder.h>
/**
@ -223,6 +224,27 @@ efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
return 0;
}
/**
* efx_filter_set_ipv6_local - specify IPv6 host, transport protocol and port
* @spec: Specification to initialise
* @proto: Transport layer protocol number
* @host: Local host address (network byte order)
* @port: Local port (network byte order)
*/
static inline int
efx_filter_set_ipv6_local(struct efx_filter_spec *spec, u8 proto,
const struct in6_addr *host, __be16 port)
{
spec->match_flags |=
EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
spec->ether_type = htons(ETH_P_IPV6);
spec->ip_proto = proto;
memcpy(spec->loc_host, host, sizeof(spec->loc_host));
spec->loc_port = port;
return 0;
}
/**
* efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports
* @spec: Specification to initialise

View File

@ -118,9 +118,14 @@
#define PTP_MIN_LENGTH 63
#define PTP_ADDRESS 0xe0000181 /* 224.0.1.129 */
#define PTP_RXFILTERS_LEN 5
#define PTP_ADDR_IPV4 0xe0000181 /* 224.0.1.129 */
#define PTP_ADDR_IPV6 {0xff, 0x0e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0x01, 0x81} /* ff0e::181 */
#define PTP_EVENT_PORT 319
#define PTP_GENERAL_PORT 320
#define PTP_ADDR_ETHER {0x01, 0x1b, 0x19, 0, 0, 0} /* 01-1B-19-00-00-00 */
/* Annoyingly the format of the version numbers are different between
* versions 1 and 2 so it isn't possible to simply look for 1 or 2.
@ -224,9 +229,8 @@ struct efx_ptp_timeset {
* @work: Work task
* @reset_required: A serious error has occurred and the PTP task needs to be
* reset (disable, enable).
* @rxfilter_event: Receive filter when operating
* @rxfilter_general: Receive filter when operating
* @rxfilter_installed: Receive filter installed
* @rxfilters: Receive filters when operating
* @rxfilters_count: Num of installed rxfilters, should be == PTP_RXFILTERS_LEN
* @config: Current timestamp configuration
* @enabled: PTP operation enabled
* @mode: Mode in which PTP operating (PTP version)
@ -295,9 +299,8 @@ struct efx_ptp_data {
struct workqueue_struct *workwq;
struct work_struct work;
bool reset_required;
u32 rxfilter_event;
u32 rxfilter_general;
bool rxfilter_installed;
u32 rxfilters[PTP_RXFILTERS_LEN];
size_t rxfilters_count;
struct hwtstamp_config config;
bool enabled;
unsigned int mode;
@ -1290,61 +1293,108 @@ static void efx_ptp_remove_multicast_filters(struct efx_nic *efx)
{
struct efx_ptp_data *ptp = efx->ptp_data;
if (ptp->rxfilter_installed) {
while (ptp->rxfilters_count) {
ptp->rxfilters_count--;
efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED,
ptp->rxfilter_general);
efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED,
ptp->rxfilter_event);
ptp->rxfilter_installed = false;
ptp->rxfilters[ptp->rxfilters_count]);
}
}
static void efx_ptp_init_filter(struct efx_nic *efx,
struct efx_filter_spec *rxfilter)
{
struct efx_channel *channel = efx->ptp_data->channel;
struct efx_rx_queue *queue = efx_channel_get_rx_queue(channel);
efx_filter_init_rx(rxfilter, EFX_FILTER_PRI_REQUIRED, 0,
efx_rx_queue_index(queue));
}
static int efx_ptp_insert_filter(struct efx_nic *efx,
struct efx_filter_spec *rxfilter)
{
struct efx_ptp_data *ptp = efx->ptp_data;
int rc = efx_filter_insert_filter(efx, rxfilter, true);
if (rc < 0)
return rc;
ptp->rxfilters[ptp->rxfilters_count] = rc;
ptp->rxfilters_count++;
return 0;
}
static int efx_ptp_insert_ipv4_filter(struct efx_nic *efx, u16 port)
{
struct efx_filter_spec rxfilter;
efx_ptp_init_filter(efx, &rxfilter);
efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP, htonl(PTP_ADDR_IPV4),
htons(port));
return efx_ptp_insert_filter(efx, &rxfilter);
}
static int efx_ptp_insert_ipv6_filter(struct efx_nic *efx, u16 port)
{
const struct in6_addr addr = {{PTP_ADDR_IPV6}};
struct efx_filter_spec rxfilter;
efx_ptp_init_filter(efx, &rxfilter);
efx_filter_set_ipv6_local(&rxfilter, IPPROTO_UDP, &addr, htons(port));
return efx_ptp_insert_filter(efx, &rxfilter);
}
static int efx_ptp_insert_eth_filter(struct efx_nic *efx)
{
const u8 addr[ETH_ALEN] = PTP_ADDR_ETHER;
struct efx_filter_spec rxfilter;
efx_ptp_init_filter(efx, &rxfilter);
efx_filter_set_eth_local(&rxfilter, EFX_FILTER_VID_UNSPEC, addr);
rxfilter.match_flags |= EFX_FILTER_MATCH_ETHER_TYPE;
rxfilter.ether_type = htons(ETH_P_1588);
return efx_ptp_insert_filter(efx, &rxfilter);
}
static int efx_ptp_insert_multicast_filters(struct efx_nic *efx)
{
struct efx_ptp_data *ptp = efx->ptp_data;
struct efx_filter_spec rxfilter;
int rc;
if (!ptp->channel || ptp->rxfilter_installed)
if (!ptp->channel || ptp->rxfilters_count)
return 0;
/* Must filter on both event and general ports to ensure
* that there is no packet re-ordering.
*/
efx_filter_init_rx(&rxfilter, EFX_FILTER_PRI_REQUIRED, 0,
efx_rx_queue_index(
efx_channel_get_rx_queue(ptp->channel)));
rc = efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP,
htonl(PTP_ADDRESS),
htons(PTP_EVENT_PORT));
if (rc != 0)
return rc;
rc = efx_filter_insert_filter(efx, &rxfilter, true);
if (rc < 0)
return rc;
ptp->rxfilter_event = rc;
efx_filter_init_rx(&rxfilter, EFX_FILTER_PRI_REQUIRED, 0,
efx_rx_queue_index(
efx_channel_get_rx_queue(ptp->channel)));
rc = efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP,
htonl(PTP_ADDRESS),
htons(PTP_GENERAL_PORT));
if (rc != 0)
goto fail;
rc = efx_filter_insert_filter(efx, &rxfilter, true);
rc = efx_ptp_insert_ipv4_filter(efx, PTP_EVENT_PORT);
if (rc < 0)
goto fail;
ptp->rxfilter_general = rc;
ptp->rxfilter_installed = true;
rc = efx_ptp_insert_ipv4_filter(efx, PTP_GENERAL_PORT);
if (rc < 0)
goto fail;
/* if the NIC supports hw timestamps by the MAC, we can support
* PTP over IPv6 and Ethernet
*/
if (efx_ptp_use_mac_tx_timestamps(efx)) {
rc = efx_ptp_insert_ipv6_filter(efx, PTP_EVENT_PORT);
if (rc < 0)
goto fail;
rc = efx_ptp_insert_ipv6_filter(efx, PTP_GENERAL_PORT);
if (rc < 0)
goto fail;
rc = efx_ptp_insert_eth_filter(efx);
if (rc < 0)
goto fail;
}
return 0;
fail:
efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED,
ptp->rxfilter_event);
efx_ptp_remove_multicast_filters(efx);
return rc;
}