diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2010-12-07 19:11:26 +0000 |
---|---|---|
committer | Ben Hutchings <bhutchings@solarflare.com> | 2010-12-07 19:11:26 +0000 |
commit | c39d35ebffeea5996a6f8fd8430fae9acfb8aeaf (patch) | |
tree | af09ac5b15429b41d4b5c54fd63c1ac769d67b39 /drivers/net/sfc/filter.c | |
parent | 8891681af928f1da795cd4bd59043e5e0fadd6c8 (diff) | |
download | kernel_samsung_smdk4412-c39d35ebffeea5996a6f8fd8430fae9acfb8aeaf.zip kernel_samsung_smdk4412-c39d35ebffeea5996a6f8fd8430fae9acfb8aeaf.tar.gz kernel_samsung_smdk4412-c39d35ebffeea5996a6f8fd8430fae9acfb8aeaf.tar.bz2 |
sfc: Generalise filter spec initialisation
Move search_depth arrays into per-table state.
Define initialisation function efx_filter_init_rx() which sets
everything apart from the match fields.
Define efx_filter_set_{ipv4_local,ipv4_full,eth_local}() to set the
match fields. This allows some simplification of callers and later
support for additional protocols and more flexible matching using
multiple calls to these functions.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/sfc/filter.c')
-rw-r--r-- | drivers/net/sfc/filter.c | 214 |
1 files changed, 176 insertions, 38 deletions
diff --git a/drivers/net/sfc/filter.c b/drivers/net/sfc/filter.c index e96e6e8..d4722c4 100644 --- a/drivers/net/sfc/filter.c +++ b/drivers/net/sfc/filter.c @@ -7,6 +7,7 @@ * by the Free Software Foundation, incorporated herein by reference. */ +#include <linux/in.h> #include "efx.h" #include "filter.h" #include "io.h" @@ -33,18 +34,19 @@ enum efx_filter_table_id { }; struct efx_filter_table { + enum efx_filter_table_id id; u32 offset; /* address of table relative to BAR */ unsigned size; /* number of entries */ unsigned step; /* step between entries */ unsigned used; /* number currently used */ unsigned long *used_bitmap; struct efx_filter_spec *spec; + unsigned search_depth[EFX_FILTER_TYPE_COUNT]; }; struct efx_filter_state { spinlock_t lock; struct efx_filter_table table[EFX_FILTER_TABLE_COUNT]; - unsigned search_depth[EFX_FILTER_TYPE_COUNT]; }; /* The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit @@ -71,68 +73,203 @@ static u16 efx_filter_increment(u32 key) } static enum efx_filter_table_id -efx_filter_type_table_id(enum efx_filter_type type) +efx_filter_spec_table_id(const struct efx_filter_spec *spec) +{ + BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_TCP_FULL >> 2)); + BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_TCP_WILD >> 2)); + BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_UDP_FULL >> 2)); + BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_UDP_WILD >> 2)); + BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_MAC_FULL >> 2)); + BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_MAC_WILD >> 2)); + EFX_BUG_ON_PARANOID(spec->type == EFX_FILTER_UNSPEC); + return spec->type >> 2; +} + +static struct efx_filter_table * +efx_filter_spec_table(struct efx_filter_state *state, + const struct efx_filter_spec *spec) { - BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_TCP_FULL >> 2)); - BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_TCP_WILD >> 2)); - BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_UDP_FULL >> 2)); - BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_UDP_WILD >> 2)); - BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_RX_MAC_FULL >> 2)); - BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_RX_MAC_WILD >> 2)); - return type >> 2; + if (spec->type == EFX_FILTER_UNSPEC) + return NULL; + else + return &state->table[efx_filter_spec_table_id(spec)]; } -static void -efx_filter_table_reset_search_depth(struct efx_filter_state *state, - enum efx_filter_table_id table_id) +static void efx_filter_table_reset_search_depth(struct efx_filter_table *table) { - memset(state->search_depth + (table_id << 2), 0, - sizeof(state->search_depth[0]) << 2); + memset(table->search_depth, 0, sizeof(table->search_depth)); } static void efx_filter_push_rx_limits(struct efx_nic *efx) { struct efx_filter_state *state = efx->filter_state; + struct efx_filter_table *table; efx_oword_t filter_ctl; efx_reado(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); + table = &state->table[EFX_FILTER_TABLE_RX_IP]; EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_FULL_SRCH_LIMIT, - state->search_depth[EFX_FILTER_RX_TCP_FULL] + + table->search_depth[EFX_FILTER_TCP_FULL] + FILTER_CTL_SRCH_FUDGE_FULL); EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_WILD_SRCH_LIMIT, - state->search_depth[EFX_FILTER_RX_TCP_WILD] + + table->search_depth[EFX_FILTER_TCP_WILD] + FILTER_CTL_SRCH_FUDGE_WILD); EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_FULL_SRCH_LIMIT, - state->search_depth[EFX_FILTER_RX_UDP_FULL] + + table->search_depth[EFX_FILTER_UDP_FULL] + FILTER_CTL_SRCH_FUDGE_FULL); EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_WILD_SRCH_LIMIT, - state->search_depth[EFX_FILTER_RX_UDP_WILD] + + table->search_depth[EFX_FILTER_UDP_WILD] + FILTER_CTL_SRCH_FUDGE_WILD); - if (state->table[EFX_FILTER_TABLE_RX_MAC].size) { + table = &state->table[EFX_FILTER_TABLE_RX_MAC]; + if (table->size) { EFX_SET_OWORD_FIELD( filter_ctl, FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT, - state->search_depth[EFX_FILTER_RX_MAC_FULL] + + table->search_depth[EFX_FILTER_MAC_FULL] + FILTER_CTL_SRCH_FUDGE_FULL); EFX_SET_OWORD_FIELD( filter_ctl, FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT, - state->search_depth[EFX_FILTER_RX_MAC_WILD] + + table->search_depth[EFX_FILTER_MAC_WILD] + FILTER_CTL_SRCH_FUDGE_WILD); } efx_writeo(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); } +static inline void __efx_filter_set_ipv4(struct efx_filter_spec *spec, + __be32 host1, __be16 port1, + __be32 host2, __be16 port2) +{ + spec->data[0] = ntohl(host1) << 16 | ntohs(port1); + spec->data[1] = ntohs(port2) << 16 | ntohl(host1) >> 16; + spec->data[2] = ntohl(host2); +} + +/** + * efx_filter_set_ipv4_local - specify IPv4 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) + */ +int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto, + __be32 host, __be16 port) +{ + __be32 host1; + __be16 port1; + + EFX_BUG_ON_PARANOID(!(spec->flags & EFX_FILTER_FLAG_RX)); + + /* This cannot currently be combined with other filtering */ + if (spec->type != EFX_FILTER_UNSPEC) + return -EPROTONOSUPPORT; + + if (port == 0) + return -EINVAL; + + switch (proto) { + case IPPROTO_TCP: + spec->type = EFX_FILTER_TCP_WILD; + break; + case IPPROTO_UDP: + spec->type = EFX_FILTER_UDP_WILD; + break; + default: + return -EPROTONOSUPPORT; + } + + /* Filter is constructed in terms of source and destination, + * with the odd wrinkle that the ports are swapped in a UDP + * wildcard filter. We need to convert from local and remote + * (= zero for wildcard) addresses. + */ + host1 = 0; + if (proto != IPPROTO_UDP) { + port1 = 0; + } else { + port1 = port; + port = 0; + } + + __efx_filter_set_ipv4(spec, host1, port1, host, port); + return 0; +} + +/** + * efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports + * @spec: Specification to initialise + * @proto: Transport layer protocol number + * @host: Local host address (network byte order) + * @port: Local port (network byte order) + * @rhost: Remote host address (network byte order) + * @rport: Remote port (network byte order) + */ +int efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto, + __be32 host, __be16 port, + __be32 rhost, __be16 rport) +{ + EFX_BUG_ON_PARANOID(!(spec->flags & EFX_FILTER_FLAG_RX)); + + /* This cannot currently be combined with other filtering */ + if (spec->type != EFX_FILTER_UNSPEC) + return -EPROTONOSUPPORT; + + if (port == 0 || rport == 0) + return -EINVAL; + + switch (proto) { + case IPPROTO_TCP: + spec->type = EFX_FILTER_TCP_FULL; + break; + case IPPROTO_UDP: + spec->type = EFX_FILTER_UDP_FULL; + break; + default: + return -EPROTONOSUPPORT; + } + + __efx_filter_set_ipv4(spec, rhost, rport, host, port); + return 0; +} + +/** + * efx_filter_set_eth_local - specify local Ethernet address and optional VID + * @spec: Specification to initialise + * @vid: VLAN ID to match, or %EFX_FILTER_VID_UNSPEC + * @addr: Local Ethernet MAC address + */ +int efx_filter_set_eth_local(struct efx_filter_spec *spec, + u16 vid, const u8 *addr) +{ + EFX_BUG_ON_PARANOID(!(spec->flags & EFX_FILTER_FLAG_RX)); + + /* This cannot currently be combined with other filtering */ + if (spec->type != EFX_FILTER_UNSPEC) + return -EPROTONOSUPPORT; + + if (vid == EFX_FILTER_VID_UNSPEC) { + spec->type = EFX_FILTER_MAC_WILD; + spec->data[0] = 0; + } else { + spec->type = EFX_FILTER_MAC_FULL; + spec->data[0] = vid; + } + + spec->data[1] = addr[2] << 24 | addr[3] << 16 | addr[4] << 8 | addr[5]; + spec->data[2] = addr[0] << 8 | addr[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) { u32 data3; - switch (efx_filter_type_table_id(spec->type)) { + switch (efx_filter_spec_table_id(spec)) { case EFX_FILTER_TABLE_RX_IP: { - bool is_udp = (spec->type == EFX_FILTER_RX_UDP_FULL || - spec->type == EFX_FILTER_RX_UDP_WILD); + bool is_udp = (spec->type == EFX_FILTER_UDP_FULL || + spec->type == EFX_FILTER_UDP_WILD); EFX_POPULATE_OWORD_7( *filter, FRF_BZ_RSS_EN, @@ -149,7 +286,7 @@ static u32 efx_filter_build(efx_oword_t *filter, struct efx_filter_spec *spec) } case EFX_FILTER_TABLE_RX_MAC: { - bool is_wild = spec->type == EFX_FILTER_RX_MAC_WILD; + bool is_wild = spec->type == EFX_FILTER_MAC_WILD; EFX_POPULATE_OWORD_8( *filter, FRF_CZ_RMFT_RSS_EN, @@ -234,23 +371,21 @@ int efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, bool replace) { struct efx_filter_state *state = efx->filter_state; - enum efx_filter_table_id table_id = - efx_filter_type_table_id(spec->type); - struct efx_filter_table *table = &state->table[table_id]; + struct efx_filter_table *table = efx_filter_spec_table(state, spec); struct efx_filter_spec *saved_spec; efx_oword_t filter; int filter_idx, depth; u32 key; int rc; - if (table->size == 0) + if (!table || table->size == 0) return -EINVAL; key = efx_filter_build(&filter, spec); netif_vdbg(efx, hw, efx->net_dev, "%s: type %d search_depth=%d", __func__, spec->type, - state->search_depth[spec->type]); + table->search_depth[spec->type]); spin_lock_bh(&state->lock); @@ -277,8 +412,8 @@ int efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, } *saved_spec = *spec; - if (state->search_depth[spec->type] < depth) { - state->search_depth[spec->type] = depth; + if (table->search_depth[spec->type] < depth) { + table->search_depth[spec->type] = depth; efx_filter_push_rx_limits(efx); } @@ -287,7 +422,7 @@ int efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, netif_vdbg(efx, hw, efx->net_dev, "%s: filter type %d index %d rxq %u set", __func__, spec->type, filter_idx, spec->dmaq_id); - rc = efx_filter_make_id(table_id, filter_idx); + rc = efx_filter_make_id(table->id, filter_idx); out: spin_unlock_bh(&state->lock); @@ -321,15 +456,16 @@ static void efx_filter_table_clear_entry(struct efx_nic *efx, int efx_filter_remove_filter(struct efx_nic *efx, struct efx_filter_spec *spec) { struct efx_filter_state *state = efx->filter_state; - enum efx_filter_table_id table_id = - efx_filter_type_table_id(spec->type); - struct efx_filter_table *table = &state->table[table_id]; + struct efx_filter_table *table = efx_filter_spec_table(state, spec); struct efx_filter_spec *saved_spec; efx_oword_t filter; int filter_idx, depth; u32 key; int rc; + if (!table) + return -EINVAL; + key = efx_filter_build(&filter, spec); spin_lock_bh(&state->lock); @@ -347,7 +483,7 @@ int efx_filter_remove_filter(struct efx_nic *efx, struct efx_filter_spec *spec) efx_filter_table_clear_entry(efx, table, filter_idx); if (table->used == 0) - efx_filter_table_reset_search_depth(state, table_id); + efx_filter_table_reset_search_depth(table); rc = 0; out: @@ -369,7 +505,7 @@ static void efx_filter_table_clear(struct efx_nic *efx, if (table->spec[filter_idx].priority <= priority) efx_filter_table_clear_entry(efx, table, filter_idx); if (table->used == 0) - efx_filter_table_reset_search_depth(state, table_id); + efx_filter_table_reset_search_depth(table); spin_unlock_bh(&state->lock); } @@ -427,6 +563,7 @@ int efx_probe_filters(struct efx_nic *efx) if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { table = &state->table[EFX_FILTER_TABLE_RX_IP]; + table->id = EFX_FILTER_TABLE_RX_IP; table->offset = FR_BZ_RX_FILTER_TBL0; table->size = FR_BZ_RX_FILTER_TBL0_ROWS; table->step = FR_BZ_RX_FILTER_TBL0_STEP; @@ -434,6 +571,7 @@ int efx_probe_filters(struct efx_nic *efx) if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { table = &state->table[EFX_FILTER_TABLE_RX_MAC]; + table->id = EFX_FILTER_TABLE_RX_MAC; table->offset = FR_CZ_RX_MAC_FILTER_TBL0; table->size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS; table->step = FR_CZ_RX_MAC_FILTER_TBL0_STEP; |