Skip to content

Commit

Permalink
sfc: remove expired unicast PTP filters
Browse files Browse the repository at this point in the history
Filters inserted to support unicast PTP mode might become unused after
some time, so we need to remove them to avoid accumulating many of them.

Actually, it would be a very unusual situation that many different
addresses are used, normally only a small set of predefined
addresses are tried. Anyway, some cleanup is necessary because
maintaining old filters forever makes very little sense.

Reported-by: Yalin Li <yalli@redhat.com>
Signed-off-by: Íñigo Huguet <ihuguet@redhat.com>
  • Loading branch information
ihuguet authored and intel-lab-lkp committed Jan 31, 2023
1 parent b979a7c commit d631c3b
Showing 1 changed file with 76 additions and 44 deletions.
120 changes: 76 additions & 44 deletions drivers/net/ethernet/sfc/ptp.c
Expand Up @@ -75,6 +75,9 @@
/* How long an unmatched event or packet can be held */
#define PKT_EVENT_LIFETIME_MS 10

/* How long unused unicast filters can be held */
#define UCAST_FILTER_EXPIRY_JIFFIES msecs_to_jiffies(30000)

/* Offsets into PTP packet for identification. These offsets are from the
* start of the IP header, not the MAC header. Note that neither PTP V1 nor
* PTP V2 permit the use of IPV4 options.
Expand Down Expand Up @@ -226,6 +229,7 @@ struct efx_ptp_rxfilter {
__be16 ether_type;
__be16 loc_port;
__be32 loc_host[4];
unsigned long expiry;
int handle;
};

Expand Down Expand Up @@ -1319,19 +1323,28 @@ static inline void efx_ptp_process_rx(struct efx_nic *efx, struct sk_buff *skb)
local_bh_enable();
}

static bool efx_ptp_filter_exists(struct list_head *ptp_list,
struct efx_filter_spec *spec)
static struct efx_ptp_rxfilter *
efx_ptp_find_filter(struct list_head *ptp_list, struct efx_filter_spec *spec)
{
struct efx_ptp_rxfilter *rxfilter;

list_for_each_entry(rxfilter, ptp_list, list) {
if (rxfilter->ether_type == spec->ether_type &&
rxfilter->loc_port == spec->loc_port &&
!memcmp(rxfilter->loc_host, spec->loc_host, sizeof(spec->loc_host)))
return true;
return rxfilter;
}

return false;
return NULL;
}

static inline void efx_ptp_remove_one_filter(struct efx_nic *efx,
struct efx_ptp_rxfilter *rxfilter)
{
efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED,
rxfilter->handle);
list_del(&rxfilter->list);
kfree(rxfilter);
}

static void efx_ptp_remove_filters(struct efx_nic *efx,
Expand All @@ -1340,10 +1353,7 @@ static void efx_ptp_remove_filters(struct efx_nic *efx,
struct efx_ptp_rxfilter *rxfilter, *tmp;

list_for_each_entry_safe(rxfilter, tmp, ptp_list, list) {
efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED,
rxfilter->handle);
list_del(&rxfilter->list);
kfree(rxfilter);
efx_ptp_remove_one_filter(efx, rxfilter);
}
}

Expand All @@ -1357,36 +1367,37 @@ static void efx_ptp_init_filter(struct efx_nic *efx,
efx_rx_queue_index(queue));
}

static int efx_ptp_insert_filter(struct efx_nic *efx,
struct list_head *ptp_list,
struct efx_filter_spec *spec)
static struct efx_ptp_rxfilter *
efx_ptp_insert_filter(struct efx_nic *efx, struct list_head *ptp_list,
struct efx_filter_spec *spec)
{
struct efx_ptp_rxfilter *rxfilter;
int rc;

if (efx_ptp_filter_exists(ptp_list, spec))
return 0;
rxfilter = efx_ptp_find_filter(ptp_list, spec);
if (rxfilter)
return rxfilter;

rc = efx_filter_insert_filter(efx, spec, true);
if (rc < 0)
return rc;
return ERR_PTR(rc);

rxfilter = kzalloc(sizeof(*rxfilter), GFP_KERNEL);
if (!rxfilter)
return -ENOMEM;
return ERR_PTR(-ENOMEM);

rxfilter->handle = rc;
rxfilter->ether_type = spec->ether_type;
rxfilter->loc_port = spec->loc_port;
memcpy(rxfilter->loc_host, spec->loc_host, sizeof(spec->loc_host));
list_add(&rxfilter->list, ptp_list);

return 0;
return rxfilter;
}

static int efx_ptp_insert_ipv4_filter(struct efx_nic *efx,
struct list_head *ptp_list,
__be32 addr, u16 port)
static struct efx_ptp_rxfilter *
efx_ptp_insert_ipv4_filter(struct efx_nic *efx, struct list_head *ptp_list,
__be32 addr, u16 port)
{
struct efx_filter_spec spec;

Expand All @@ -1395,9 +1406,9 @@ static int efx_ptp_insert_ipv4_filter(struct efx_nic *efx,
return efx_ptp_insert_filter(efx, ptp_list, &spec);
}

static int efx_ptp_insert_ipv6_filter(struct efx_nic *efx,
struct list_head *ptp_list,
struct in6_addr *addr, u16 port)
static struct efx_ptp_rxfilter *
efx_ptp_insert_ipv6_filter(struct efx_nic *efx, struct list_head *ptp_list,
struct in6_addr *addr, u16 port)
{
struct efx_filter_spec spec;

Expand All @@ -1406,7 +1417,8 @@ static int efx_ptp_insert_ipv6_filter(struct efx_nic *efx,
return efx_ptp_insert_filter(efx, ptp_list, &spec);
}

static int efx_ptp_insert_eth_multicast_filter(struct efx_nic *efx)
static struct efx_ptp_rxfilter *
efx_ptp_insert_eth_multicast_filter(struct efx_nic *efx)
{
const u8 addr[ETH_ALEN] = PTP_ADDR_ETHER;
struct efx_filter_spec spec;
Expand All @@ -1421,7 +1433,7 @@ static int efx_ptp_insert_eth_multicast_filter(struct efx_nic *efx)
static int efx_ptp_insert_multicast_filters(struct efx_nic *efx)
{
struct efx_ptp_data *ptp = efx->ptp_data;
int rc;
struct efx_ptp_rxfilter *rc;

if (!ptp->channel || !list_empty(&ptp->rxfilters_mcast))
return 0;
Expand All @@ -1431,12 +1443,12 @@ static int efx_ptp_insert_multicast_filters(struct efx_nic *efx)
*/
rc = efx_ptp_insert_ipv4_filter(efx, &ptp->rxfilters_mcast,
htonl(PTP_ADDR_IPV4), PTP_EVENT_PORT);
if (rc < 0)
if (IS_ERR(rc))
goto fail;

rc = efx_ptp_insert_ipv4_filter(efx, &ptp->rxfilters_mcast,
htonl(PTP_ADDR_IPV4), PTP_GENERAL_PORT);
if (rc < 0)
if (IS_ERR(rc))
goto fail;

/* if the NIC supports hw timestamps by the MAC, we can support
Expand All @@ -1447,24 +1459,24 @@ static int efx_ptp_insert_multicast_filters(struct efx_nic *efx)

rc = efx_ptp_insert_ipv6_filter(efx, &ptp->rxfilters_mcast,
&ipv6_addr, PTP_EVENT_PORT);
if (rc < 0)
if (IS_ERR(rc))
goto fail;

rc = efx_ptp_insert_ipv6_filter(efx, &ptp->rxfilters_mcast,
&ipv6_addr, PTP_GENERAL_PORT);
if (rc < 0)
if (IS_ERR(rc))
goto fail;

rc = efx_ptp_insert_eth_multicast_filter(efx);
if (rc < 0)
if (IS_ERR(rc))
goto fail;
}

return 0;

fail:
efx_ptp_remove_filters(efx, &ptp->rxfilters_mcast);
return rc;
return PTR_ERR(rc);
}

static bool efx_ptp_valid_unicast_event_pkt(struct sk_buff *skb)
Expand All @@ -1483,36 +1495,44 @@ static int efx_ptp_insert_unicast_filter(struct efx_nic *efx,
struct sk_buff *skb)
{
struct efx_ptp_data *ptp = efx->ptp_data;
int rc;
struct efx_ptp_rxfilter *rxfilter;

if (!efx_ptp_valid_unicast_event_pkt(skb))
return -EINVAL;

if (skb->protocol == htons(ETH_P_IP)) {
__be32 addr = ip_hdr(skb)->saddr;

rc = efx_ptp_insert_ipv4_filter(efx, &ptp->rxfilters_ucast,
addr, PTP_EVENT_PORT);
if (rc < 0)
rxfilter = efx_ptp_insert_ipv4_filter(efx, &ptp->rxfilters_ucast,
addr, PTP_EVENT_PORT);
if (IS_ERR(rxfilter))
goto fail;

rc = efx_ptp_insert_ipv4_filter(efx, &ptp->rxfilters_ucast,
addr, PTP_GENERAL_PORT);
if (rc < 0)
rxfilter->expiry = jiffies + UCAST_FILTER_EXPIRY_JIFFIES;

rxfilter = efx_ptp_insert_ipv4_filter(efx, &ptp->rxfilters_ucast,
addr, PTP_GENERAL_PORT);
if (rxfilter < 0)
goto fail;

rxfilter->expiry = jiffies + UCAST_FILTER_EXPIRY_JIFFIES;
} else if (efx_ptp_use_mac_tx_timestamps(efx)) {
/* IPv6 PTP only supported by devices with MAC hw timestamp */
struct in6_addr *addr = &ipv6_hdr(skb)->saddr;

rc = efx_ptp_insert_ipv6_filter(efx, &ptp->rxfilters_ucast,
addr, PTP_EVENT_PORT);
if (rc < 0)
rxfilter = efx_ptp_insert_ipv6_filter(efx, &ptp->rxfilters_ucast,
addr, PTP_EVENT_PORT);
if (IS_ERR(rxfilter))
goto fail;

rc = efx_ptp_insert_ipv6_filter(efx, &ptp->rxfilters_ucast,
addr, PTP_GENERAL_PORT);
if (rc < 0)
rxfilter->expiry = jiffies + UCAST_FILTER_EXPIRY_JIFFIES;

rxfilter = efx_ptp_insert_ipv6_filter(efx, &ptp->rxfilters_ucast,
addr, PTP_GENERAL_PORT);
if (IS_ERR(rxfilter))
goto fail;

rxfilter->expiry = jiffies + UCAST_FILTER_EXPIRY_JIFFIES;
} else {
return -EOPNOTSUPP;
}
Expand All @@ -1521,7 +1541,18 @@ static int efx_ptp_insert_unicast_filter(struct efx_nic *efx,

fail:
efx_ptp_remove_filters(efx, &ptp->rxfilters_ucast);
return rc;
return PTR_ERR(rxfilter);
}

static void efx_ptp_drop_expired_unicast_filters(struct efx_nic *efx)
{
struct efx_ptp_data *ptp = efx->ptp_data;
struct efx_ptp_rxfilter *rxfilter, *tmp;

list_for_each_entry_safe(rxfilter, tmp, &ptp->rxfilters_ucast, list) {
if (time_is_before_jiffies(rxfilter->expiry))
efx_ptp_remove_one_filter(efx, rxfilter);
}
}

static int efx_ptp_start(struct efx_nic *efx)
Expand Down Expand Up @@ -1615,6 +1646,7 @@ static void efx_ptp_worker(struct work_struct *work)
}

efx_ptp_drop_time_expired_events(efx);
efx_ptp_drop_expired_unicast_filters(efx);

__skb_queue_head_init(&tempq);
efx_ptp_process_events(efx, &tempq);
Expand Down

0 comments on commit d631c3b

Please sign in to comment.