Skip to content

Commit

Permalink
sfc: Add support for retrieving and removing filters by ID
Browse files Browse the repository at this point in the history
These new functions will support an implementation of the ethtool
RX NFC rules API.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Ben Hutchings authored and David S. Miller committed Jan 4, 2012
1 parent 3532650 commit 1a6281a
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 0 deletions.
12 changes: 12 additions & 0 deletions drivers/net/ethernet/sfc/efx.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,20 @@ extern s32 efx_filter_insert_filter(struct efx_nic *efx,
bool replace);
extern int efx_filter_remove_filter(struct efx_nic *efx,
struct efx_filter_spec *spec);
extern int efx_filter_remove_id_safe(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 filter_id);
extern int efx_filter_get_filter_safe(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 filter_id, struct efx_filter_spec *);
extern void efx_filter_clear_rx(struct efx_nic *efx,
enum efx_filter_priority priority);
extern u32 efx_filter_count_rx_used(struct efx_nic *efx,
enum efx_filter_priority priority);
extern u32 efx_filter_get_rx_id_limit(struct efx_nic *efx);
extern s32 efx_filter_get_rx_ids(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 *buf, u32 size);
#ifdef CONFIG_RFS_ACCEL
extern int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
u16 rxq_index, u32 flow_id);
Expand Down
247 changes: 247 additions & 0 deletions drivers/net/ethernet/sfc/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,16 @@ static inline void __efx_filter_set_ipv4(struct efx_filter_spec *spec,
spec->data[2] = ntohl(host2);
}

static inline void __efx_filter_get_ipv4(const struct efx_filter_spec *spec,
__be32 *host1, __be16 *port1,
__be32 *host2, __be16 *port2)
{
*host1 = htonl(spec->data[0] >> 16 | spec->data[1] << 16);
*port1 = htons(spec->data[0]);
*host2 = htonl(spec->data[2]);
*port2 = htons(spec->data[1] >> 16);
}

/**
* efx_filter_set_ipv4_local - specify IPv4 host, transport protocol and port
* @spec: Specification to initialise
Expand Down Expand Up @@ -205,6 +215,26 @@ int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
return 0;
}

int efx_filter_get_ipv4_local(const struct efx_filter_spec *spec,
u8 *proto, __be32 *host, __be16 *port)
{
__be32 host1;
__be16 port1;

switch (spec->type) {
case EFX_FILTER_TCP_WILD:
*proto = IPPROTO_TCP;
__efx_filter_get_ipv4(spec, &host1, &port1, host, port);
return 0;
case EFX_FILTER_UDP_WILD:
*proto = IPPROTO_UDP;
__efx_filter_get_ipv4(spec, &host1, port, host, &port1);
return 0;
default:
return -EINVAL;
}
}

/**
* efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports
* @spec: Specification to initialise
Expand Down Expand Up @@ -242,6 +272,25 @@ int efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto,
return 0;
}

int efx_filter_get_ipv4_full(const struct efx_filter_spec *spec,
u8 *proto, __be32 *host, __be16 *port,
__be32 *rhost, __be16 *rport)
{
switch (spec->type) {
case EFX_FILTER_TCP_FULL:
*proto = IPPROTO_TCP;
break;
case EFX_FILTER_UDP_FULL:
*proto = IPPROTO_UDP;
break;
default:
return -EINVAL;
}

__efx_filter_get_ipv4(spec, rhost, rport, host, port);
return 0;
}

/**
* efx_filter_set_eth_local - specify local Ethernet address and optional VID
* @spec: Specification to initialise
Expand Down Expand Up @@ -270,6 +319,29 @@ int efx_filter_set_eth_local(struct efx_filter_spec *spec,
return 0;
}

int efx_filter_get_eth_local(const struct efx_filter_spec *spec,
u16 *vid, u8 *addr)
{
switch (spec->type) {
case EFX_FILTER_MAC_WILD:
*vid = EFX_FILTER_VID_UNSPEC;
break;
case EFX_FILTER_MAC_FULL:
*vid = spec->data[0];
break;
default:
return -EINVAL;
}

addr[0] = spec->data[2] >> 8;
addr[1] = spec->data[2];
addr[2] = spec->data[1] >> 24;
addr[3] = spec->data[1] >> 16;
addr[4] = spec->data[1] >> 8;
addr[5] = spec->data[1];
return 0;
}

/* Build a filter entry and return its n-tuple key. */
static u32 efx_filter_build(efx_oword_t *filter, struct efx_filter_spec *spec)
{
Expand Down Expand Up @@ -407,6 +479,20 @@ static inline u8 efx_filter_id_flags(u32 id)
EFX_FILTER_FLAG_RX;
}

u32 efx_filter_get_rx_id_limit(struct efx_nic *efx)
{
struct efx_filter_state *state = efx->filter_state;

if (state->table[EFX_FILTER_TABLE_RX_MAC].size != 0)
return ((EFX_FILTER_TABLE_RX_MAC + 1) << EFX_FILTER_INDEX_WIDTH)
+ state->table[EFX_FILTER_TABLE_RX_MAC].size;
else if (state->table[EFX_FILTER_TABLE_RX_IP].size != 0)
return ((EFX_FILTER_TABLE_RX_IP + 1) << EFX_FILTER_INDEX_WIDTH)
+ state->table[EFX_FILTER_TABLE_RX_IP].size;
else
return 0;
}

/**
* efx_filter_insert_filter - add or replace a filter
* @efx: NIC in which to insert the filter
Expand Down Expand Up @@ -495,6 +581,105 @@ static void efx_filter_table_clear_entry(struct efx_nic *efx,
}
}

/**
* efx_filter_remove_id_safe - remove a filter by ID, carefully
* @efx: NIC from which to remove the filter
* @priority: Priority of filter, as passed to @efx_filter_insert_filter
* @filter_id: ID of filter, as returned by @efx_filter_insert_filter
*
* This function will range-check @filter_id, so it is safe to call
* with a value passed from userland.
*/
int efx_filter_remove_id_safe(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 filter_id)
{
struct efx_filter_state *state = efx->filter_state;
enum efx_filter_table_id table_id;
struct efx_filter_table *table;
unsigned int filter_idx;
struct efx_filter_spec *spec;
u8 filter_flags;
int rc;

table_id = efx_filter_id_table_id(filter_id);
if ((unsigned int)table_id >= EFX_FILTER_TABLE_COUNT)
return -ENOENT;
table = &state->table[table_id];

filter_idx = efx_filter_id_index(filter_id);
if (filter_idx >= table->size)
return -ENOENT;
spec = &table->spec[filter_idx];

filter_flags = efx_filter_id_flags(filter_id);

spin_lock_bh(&state->lock);

if (test_bit(filter_idx, table->used_bitmap) &&
spec->priority == priority && spec->flags == filter_flags) {
efx_filter_table_clear_entry(efx, table, filter_idx);
if (table->used == 0)
efx_filter_table_reset_search_depth(table);
rc = 0;
} else {
rc = -ENOENT;
}

spin_unlock_bh(&state->lock);

return rc;
}

/**
* efx_filter_get_filter_safe - retrieve a filter by ID, carefully
* @efx: NIC from which to remove the filter
* @priority: Priority of filter, as passed to @efx_filter_insert_filter
* @filter_id: ID of filter, as returned by @efx_filter_insert_filter
* @spec: Buffer in which to store filter specification
*
* This function will range-check @filter_id, so it is safe to call
* with a value passed from userland.
*/
int efx_filter_get_filter_safe(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 filter_id, struct efx_filter_spec *spec_buf)
{
struct efx_filter_state *state = efx->filter_state;
enum efx_filter_table_id table_id;
struct efx_filter_table *table;
struct efx_filter_spec *spec;
unsigned int filter_idx;
u8 filter_flags;
int rc;

table_id = efx_filter_id_table_id(filter_id);
if ((unsigned int)table_id >= EFX_FILTER_TABLE_COUNT)
return -ENOENT;
table = &state->table[table_id];

filter_idx = efx_filter_id_index(filter_id);
if (filter_idx >= table->size)
return -ENOENT;
spec = &table->spec[filter_idx];

filter_flags = efx_filter_id_flags(filter_id);

spin_lock_bh(&state->lock);

if (test_bit(filter_idx, table->used_bitmap) &&
spec->priority == priority && spec->flags == filter_flags) {
*spec_buf = *spec;
rc = 0;
} else {
rc = -ENOENT;
}

spin_unlock_bh(&state->lock);

return rc;
}

/**
* efx_filter_remove_filter - remove a filter by specification
* @efx: NIC from which to remove the filter
Expand Down Expand Up @@ -571,6 +756,68 @@ void efx_filter_clear_rx(struct efx_nic *efx, enum efx_filter_priority priority)
efx_filter_table_clear(efx, EFX_FILTER_TABLE_RX_MAC, priority);
}

u32 efx_filter_count_rx_used(struct efx_nic *efx,
enum efx_filter_priority priority)
{
struct efx_filter_state *state = efx->filter_state;
enum efx_filter_table_id table_id;
struct efx_filter_table *table;
unsigned int filter_idx;
u32 count = 0;

spin_lock_bh(&state->lock);

for (table_id = EFX_FILTER_TABLE_RX_IP;
table_id <= EFX_FILTER_TABLE_RX_MAC;
table_id++) {
table = &state->table[table_id];
for (filter_idx = 0; filter_idx < table->size; filter_idx++) {
if (test_bit(filter_idx, table->used_bitmap) &&
table->spec[filter_idx].priority == priority)
++count;
}
}

spin_unlock_bh(&state->lock);

return count;
}

s32 efx_filter_get_rx_ids(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 *buf, u32 size)
{
struct efx_filter_state *state = efx->filter_state;
enum efx_filter_table_id table_id;
struct efx_filter_table *table;
unsigned int filter_idx;
s32 count = 0;

spin_lock_bh(&state->lock);

for (table_id = EFX_FILTER_TABLE_RX_IP;
table_id <= EFX_FILTER_TABLE_RX_MAC;
table_id++) {
table = &state->table[table_id];
for (filter_idx = 0; filter_idx < table->size; filter_idx++) {
if (test_bit(filter_idx, table->used_bitmap) &&
table->spec[filter_idx].priority == priority) {
if (count == size) {
count = -EMSGSIZE;
goto out;
}
buf[count++] = efx_filter_make_id(
table_id, filter_idx,
table->spec[filter_idx].flags);
}
}
}
out:
spin_unlock_bh(&state->lock);

return count;
}

/* Restore filter stater after reset */
void efx_restore_filters(struct efx_nic *efx)
{
Expand Down
7 changes: 7 additions & 0 deletions drivers/net/ethernet/sfc/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,18 @@ static inline void efx_filter_init_rx(struct efx_filter_spec *spec,

extern int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
__be32 host, __be16 port);
extern int efx_filter_get_ipv4_local(const struct efx_filter_spec *spec,
u8 *proto, __be32 *host, __be16 *port);
extern int efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto,
__be32 host, __be16 port,
__be32 rhost, __be16 rport);
extern int efx_filter_get_ipv4_full(const struct efx_filter_spec *spec,
u8 *proto, __be32 *host, __be16 *port,
__be32 *rhost, __be16 *rport);
extern int efx_filter_set_eth_local(struct efx_filter_spec *spec,
u16 vid, const u8 *addr);
extern int efx_filter_get_eth_local(const struct efx_filter_spec *spec,
u16 *vid, u8 *addr);
enum {
EFX_FILTER_VID_UNSPEC = 0xffff,
};
Expand Down

0 comments on commit 1a6281a

Please sign in to comment.