Commit 98ba8108 authored by David S. Miller's avatar David S. Miller

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 bd4a2697
("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: default avatarEdward Cree <ecree.xilinx@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 13248b97 e4616f64
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/in6.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
/** /**
...@@ -223,6 +224,27 @@ efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto, ...@@ -223,6 +224,27 @@ efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
return 0; 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 * efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports
* @spec: Specification to initialise * @spec: Specification to initialise
......
...@@ -118,9 +118,14 @@ ...@@ -118,9 +118,14 @@
#define PTP_MIN_LENGTH 63 #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_EVENT_PORT 319
#define PTP_GENERAL_PORT 320 #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 /* 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. * versions 1 and 2 so it isn't possible to simply look for 1 or 2.
...@@ -224,9 +229,8 @@ struct efx_ptp_timeset { ...@@ -224,9 +229,8 @@ struct efx_ptp_timeset {
* @work: Work task * @work: Work task
* @reset_required: A serious error has occurred and the PTP task needs to be * @reset_required: A serious error has occurred and the PTP task needs to be
* reset (disable, enable). * reset (disable, enable).
* @rxfilter_event: Receive filter when operating * @rxfilters: Receive filters when operating
* @rxfilter_general: Receive filter when operating * @rxfilters_count: Num of installed rxfilters, should be == PTP_RXFILTERS_LEN
* @rxfilter_installed: Receive filter installed
* @config: Current timestamp configuration * @config: Current timestamp configuration
* @enabled: PTP operation enabled * @enabled: PTP operation enabled
* @mode: Mode in which PTP operating (PTP version) * @mode: Mode in which PTP operating (PTP version)
...@@ -295,9 +299,8 @@ struct efx_ptp_data { ...@@ -295,9 +299,8 @@ struct efx_ptp_data {
struct workqueue_struct *workwq; struct workqueue_struct *workwq;
struct work_struct work; struct work_struct work;
bool reset_required; bool reset_required;
u32 rxfilter_event; u32 rxfilters[PTP_RXFILTERS_LEN];
u32 rxfilter_general; size_t rxfilters_count;
bool rxfilter_installed;
struct hwtstamp_config config; struct hwtstamp_config config;
bool enabled; bool enabled;
unsigned int mode; unsigned int mode;
...@@ -1290,61 +1293,108 @@ static void efx_ptp_remove_multicast_filters(struct efx_nic *efx) ...@@ -1290,61 +1293,108 @@ static void efx_ptp_remove_multicast_filters(struct efx_nic *efx)
{ {
struct efx_ptp_data *ptp = efx->ptp_data; struct efx_ptp_data *ptp = efx->ptp_data;
if (ptp->rxfilter_installed) { while (ptp->rxfilters_count) {
efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, ptp->rxfilters_count--;
ptp->rxfilter_general);
efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED,
ptp->rxfilter_event); ptp->rxfilters[ptp->rxfilters_count]);
ptp->rxfilter_installed = false;
} }
} }
static int efx_ptp_insert_multicast_filters(struct efx_nic *efx) 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; 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; 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;
int rc; int rc;
if (!ptp->channel || ptp->rxfilter_installed) if (!ptp->channel || ptp->rxfilters_count)
return 0; return 0;
/* Must filter on both event and general ports to ensure /* Must filter on both event and general ports to ensure
* that there is no packet re-ordering. * that there is no packet re-ordering.
*/ */
efx_filter_init_rx(&rxfilter, EFX_FILTER_PRI_REQUIRED, 0, rc = efx_ptp_insert_ipv4_filter(efx, PTP_EVENT_PORT);
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) 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; goto fail;
rc = efx_filter_insert_filter(efx, &rxfilter, true); rc = efx_ptp_insert_ipv4_filter(efx, PTP_GENERAL_PORT);
if (rc < 0) if (rc < 0)
goto fail; goto fail;
ptp->rxfilter_general = rc;
ptp->rxfilter_installed = true; /* 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; return 0;
fail: fail:
efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, efx_ptp_remove_multicast_filters(efx);
ptp->rxfilter_event);
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment