diff options
| author | Ben Hutchings <bhutchings@solarflare.com> | 2010-09-20 04:43:07 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2010-09-21 17:58:00 -0400 |
| commit | 64eebcfd899a5d2ebe211a593ec13ec24630f1a3 (patch) | |
| tree | b147a37d3b4a586464908221b9ea18f80c34ee46 | |
| parent | a77f5db361ed9953b5b749353ea2c7fed2bf8d93 (diff) | |
sfc: Add filter table management
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | drivers/net/sfc/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/sfc/efx.c | 11 | ||||
| -rw-r--r-- | drivers/net/sfc/efx.h | 14 | ||||
| -rw-r--r-- | drivers/net/sfc/filter.c | 445 | ||||
| -rw-r--r-- | drivers/net/sfc/filter.h | 189 | ||||
| -rw-r--r-- | drivers/net/sfc/net_driver.h | 4 | ||||
| -rw-r--r-- | drivers/net/sfc/regs.h | 14 |
7 files changed, 678 insertions, 1 deletions
diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile index 1047b19c60a5..fd9272b5873a 100644 --- a/drivers/net/sfc/Makefile +++ b/drivers/net/sfc/Makefile | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o \ | 1 | sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o filter.o \ |
| 2 | falcon_gmac.o falcon_xmac.o mcdi_mac.o \ | 2 | falcon_gmac.o falcon_xmac.o mcdi_mac.o \ |
| 3 | selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ | 3 | selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ |
| 4 | tenxpress.o falcon_boards.o mcdi.o mcdi_phy.o | 4 | tenxpress.o falcon_boards.o mcdi.o mcdi_phy.o |
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index f702f1fb63b6..4a1c93f165e6 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c | |||
| @@ -1357,8 +1357,17 @@ static int efx_probe_all(struct efx_nic *efx) | |||
| 1357 | if (rc) | 1357 | if (rc) |
| 1358 | goto fail3; | 1358 | goto fail3; |
| 1359 | 1359 | ||
| 1360 | rc = efx_probe_filters(efx); | ||
| 1361 | if (rc) { | ||
| 1362 | netif_err(efx, probe, efx->net_dev, | ||
| 1363 | "failed to create filter tables\n"); | ||
| 1364 | goto fail4; | ||
| 1365 | } | ||
| 1366 | |||
| 1360 | return 0; | 1367 | return 0; |
| 1361 | 1368 | ||
| 1369 | fail4: | ||
| 1370 | efx_remove_channels(efx); | ||
| 1362 | fail3: | 1371 | fail3: |
| 1363 | efx_remove_port(efx); | 1372 | efx_remove_port(efx); |
| 1364 | fail2: | 1373 | fail2: |
| @@ -1489,6 +1498,7 @@ static void efx_stop_all(struct efx_nic *efx) | |||
| 1489 | 1498 | ||
| 1490 | static void efx_remove_all(struct efx_nic *efx) | 1499 | static void efx_remove_all(struct efx_nic *efx) |
| 1491 | { | 1500 | { |
| 1501 | efx_remove_filters(efx); | ||
| 1492 | efx_remove_channels(efx); | 1502 | efx_remove_channels(efx); |
| 1493 | efx_remove_port(efx); | 1503 | efx_remove_port(efx); |
| 1494 | efx_remove_nic(efx); | 1504 | efx_remove_nic(efx); |
| @@ -2002,6 +2012,7 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok) | |||
| 2002 | efx->mac_op->reconfigure(efx); | 2012 | efx->mac_op->reconfigure(efx); |
| 2003 | 2013 | ||
| 2004 | efx_init_channels(efx); | 2014 | efx_init_channels(efx); |
| 2015 | efx_restore_filters(efx); | ||
| 2005 | 2016 | ||
| 2006 | mutex_unlock(&efx->spi_lock); | 2017 | mutex_unlock(&efx->spi_lock); |
| 2007 | mutex_unlock(&efx->mac_lock); | 2018 | mutex_unlock(&efx->mac_lock); |
diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index e783c0fedfd8..f502b14eb22c 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #define EFX_EFX_H | 12 | #define EFX_EFX_H |
| 13 | 13 | ||
| 14 | #include "net_driver.h" | 14 | #include "net_driver.h" |
| 15 | #include "filter.h" | ||
| 15 | 16 | ||
| 16 | /* PCI IDs */ | 17 | /* PCI IDs */ |
| 17 | #define EFX_VENDID_SFC 0x1924 | 18 | #define EFX_VENDID_SFC 0x1924 |
| @@ -64,6 +65,19 @@ extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue); | |||
| 64 | * skb. Falcon/A1 may require up to three descriptors per skb_frag. */ | 65 | * skb. Falcon/A1 may require up to three descriptors per skb_frag. */ |
| 65 | #define EFX_MIN_RING_SIZE (roundup_pow_of_two(2 * 3 * MAX_SKB_FRAGS)) | 66 | #define EFX_MIN_RING_SIZE (roundup_pow_of_two(2 * 3 * MAX_SKB_FRAGS)) |
| 66 | 67 | ||
| 68 | /* Filters */ | ||
| 69 | extern int efx_probe_filters(struct efx_nic *efx); | ||
| 70 | extern void efx_restore_filters(struct efx_nic *efx); | ||
| 71 | extern void efx_remove_filters(struct efx_nic *efx); | ||
| 72 | extern int efx_filter_insert_filter(struct efx_nic *efx, | ||
| 73 | struct efx_filter_spec *spec, | ||
| 74 | bool replace); | ||
| 75 | extern int efx_filter_remove_filter(struct efx_nic *efx, | ||
| 76 | struct efx_filter_spec *spec); | ||
| 77 | extern void efx_filter_table_clear(struct efx_nic *efx, | ||
| 78 | enum efx_filter_table_id table_id, | ||
| 79 | enum efx_filter_priority priority); | ||
| 80 | |||
| 67 | /* Channels */ | 81 | /* Channels */ |
| 68 | extern void efx_process_channel_now(struct efx_channel *channel); | 82 | extern void efx_process_channel_now(struct efx_channel *channel); |
| 69 | extern int | 83 | extern int |
diff --git a/drivers/net/sfc/filter.c b/drivers/net/sfc/filter.c new file mode 100644 index 000000000000..abc884d09d57 --- /dev/null +++ b/drivers/net/sfc/filter.c | |||
| @@ -0,0 +1,445 @@ | |||
| 1 | /**************************************************************************** | ||
| 2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
| 3 | * Copyright 2005-2010 Solarflare Communications Inc. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published | ||
| 7 | * by the Free Software Foundation, incorporated herein by reference. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include "efx.h" | ||
| 11 | #include "filter.h" | ||
| 12 | #include "io.h" | ||
| 13 | #include "nic.h" | ||
| 14 | #include "regs.h" | ||
| 15 | |||
| 16 | /* "Fudge factors" - difference between programmed value and actual depth. | ||
| 17 | * Due to pipelined implementation we need to program H/W with a value that | ||
| 18 | * is larger than the hop limit we want. | ||
| 19 | */ | ||
| 20 | #define FILTER_CTL_SRCH_FUDGE_WILD 3 | ||
| 21 | #define FILTER_CTL_SRCH_FUDGE_FULL 1 | ||
| 22 | |||
| 23 | struct efx_filter_table { | ||
| 24 | u32 offset; /* address of table relative to BAR */ | ||
| 25 | unsigned size; /* number of entries */ | ||
| 26 | unsigned step; /* step between entries */ | ||
| 27 | unsigned used; /* number currently used */ | ||
| 28 | unsigned long *used_bitmap; | ||
| 29 | struct efx_filter_spec *spec; | ||
| 30 | }; | ||
| 31 | |||
| 32 | struct efx_filter_state { | ||
| 33 | spinlock_t lock; | ||
| 34 | struct efx_filter_table table[EFX_FILTER_TABLE_COUNT]; | ||
| 35 | unsigned search_depth[EFX_FILTER_TYPE_COUNT]; | ||
| 36 | }; | ||
| 37 | |||
| 38 | /* The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit | ||
| 39 | * key derived from the n-tuple. The initial LFSR state is 0xffff. */ | ||
| 40 | static u16 efx_filter_hash(u32 key) | ||
| 41 | { | ||
| 42 | u16 tmp; | ||
| 43 | |||
| 44 | /* First 16 rounds */ | ||
| 45 | tmp = 0x1fff ^ key >> 16; | ||
| 46 | tmp = tmp ^ tmp >> 3 ^ tmp >> 6; | ||
| 47 | tmp = tmp ^ tmp >> 9; | ||
| 48 | /* Last 16 rounds */ | ||
| 49 | tmp = tmp ^ tmp << 13 ^ key; | ||
| 50 | tmp = tmp ^ tmp >> 3 ^ tmp >> 6; | ||
| 51 | return tmp ^ tmp >> 9; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* To allow for hash collisions, filter search continues at these | ||
| 55 | * increments from the first possible entry selected by the hash. */ | ||
| 56 | static u16 efx_filter_increment(u32 key) | ||
| 57 | { | ||
| 58 | return key * 2 - 1; | ||
| 59 | } | ||
| 60 | |||
| 61 | static enum efx_filter_table_id | ||
| 62 | efx_filter_type_table_id(enum efx_filter_type type) | ||
| 63 | { | ||
| 64 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_TCP_FULL >> 2)); | ||
| 65 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_TCP_WILD >> 2)); | ||
| 66 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_UDP_FULL >> 2)); | ||
| 67 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_RX_UDP_WILD >> 2)); | ||
| 68 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_RX_MAC_FULL >> 2)); | ||
| 69 | BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_RX_MAC_WILD >> 2)); | ||
| 70 | return type >> 2; | ||
| 71 | } | ||
| 72 | |||
| 73 | static void | ||
| 74 | efx_filter_table_reset_search_depth(struct efx_filter_state *state, | ||
| 75 | enum efx_filter_table_id table_id) | ||
| 76 | { | ||
| 77 | memset(state->search_depth + (table_id << 2), 0, | ||
| 78 | sizeof(state->search_depth[0]) << 2); | ||
| 79 | } | ||
| 80 | |||
| 81 | static void efx_filter_push_rx_limits(struct efx_nic *efx) | ||
| 82 | { | ||
| 83 | struct efx_filter_state *state = efx->filter_state; | ||
| 84 | efx_oword_t filter_ctl; | ||
| 85 | |||
| 86 | efx_reado(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); | ||
| 87 | |||
| 88 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_FULL_SRCH_LIMIT, | ||
| 89 | state->search_depth[EFX_FILTER_RX_TCP_FULL] + | ||
| 90 | FILTER_CTL_SRCH_FUDGE_FULL); | ||
| 91 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_WILD_SRCH_LIMIT, | ||
| 92 | state->search_depth[EFX_FILTER_RX_TCP_WILD] + | ||
| 93 | FILTER_CTL_SRCH_FUDGE_WILD); | ||
| 94 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_FULL_SRCH_LIMIT, | ||
| 95 | state->search_depth[EFX_FILTER_RX_UDP_FULL] + | ||
| 96 | FILTER_CTL_SRCH_FUDGE_FULL); | ||
| 97 | EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_WILD_SRCH_LIMIT, | ||
| 98 | state->search_depth[EFX_FILTER_RX_UDP_WILD] + | ||
| 99 | FILTER_CTL_SRCH_FUDGE_WILD); | ||
| 100 | |||
| 101 | if (state->table[EFX_FILTER_TABLE_RX_MAC].size) { | ||
| 102 | EFX_SET_OWORD_FIELD( | ||
| 103 | filter_ctl, FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT, | ||
| 104 | state->search_depth[EFX_FILTER_RX_MAC_FULL] + | ||
| 105 | FILTER_CTL_SRCH_FUDGE_FULL); | ||
| 106 | EFX_SET_OWORD_FIELD( | ||
| 107 | filter_ctl, FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT, | ||
| 108 | state->search_depth[EFX_FILTER_RX_MAC_WILD] + | ||
| 109 | FILTER_CTL_SRCH_FUDGE_WILD); | ||
| 110 | } | ||
| 111 | |||
| 112 | efx_writeo(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Build a filter entry and return its n-tuple key. */ | ||
| 116 | static u32 efx_filter_build(efx_oword_t *filter, struct efx_filter_spec *spec) | ||
| 117 | { | ||
| 118 | u32 data3; | ||
| 119 | |||
| 120 | switch (efx_filter_type_table_id(spec->type)) { | ||
| 121 | case EFX_FILTER_TABLE_RX_IP: { | ||
| 122 | bool is_udp = (spec->type == EFX_FILTER_RX_UDP_FULL || | ||
| 123 | spec->type == EFX_FILTER_RX_UDP_WILD); | ||
| 124 | EFX_POPULATE_OWORD_7( | ||
| 125 | *filter, | ||
| 126 | FRF_BZ_RSS_EN, | ||
| 127 | !!(spec->flags & EFX_FILTER_FLAG_RX_RSS), | ||
| 128 | FRF_BZ_SCATTER_EN, | ||
| 129 | !!(spec->flags & EFX_FILTER_FLAG_RX_SCATTER), | ||
| 130 | FRF_BZ_TCP_UDP, is_udp, | ||
| 131 | FRF_BZ_RXQ_ID, spec->dmaq_id, | ||
| 132 | EFX_DWORD_2, spec->data[2], | ||
| 133 | EFX_DWORD_1, spec->data[1], | ||
| 134 | EFX_DWORD_0, spec->data[0]); | ||
| 135 | data3 = is_udp; | ||
| 136 | break; | ||
| 137 | } | ||
| 138 | |||
| 139 | case EFX_FILTER_TABLE_RX_MAC: { | ||
| 140 | bool is_wild = spec->type == EFX_FILTER_RX_MAC_WILD; | ||
| 141 | EFX_POPULATE_OWORD_8( | ||
| 142 | *filter, | ||
| 143 | FRF_CZ_RMFT_RSS_EN, | ||
| 144 | !!(spec->flags & EFX_FILTER_FLAG_RX_RSS), | ||
| 145 | FRF_CZ_RMFT_SCATTER_EN, | ||
| 146 | !!(spec->flags & EFX_FILTER_FLAG_RX_SCATTER), | ||
| 147 | FRF_CZ_RMFT_IP_OVERRIDE, | ||
| 148 | !!(spec->flags & EFX_FILTER_FLAG_RX_OVERRIDE_IP), | ||
| 149 | FRF_CZ_RMFT_RXQ_ID, spec->dmaq_id, | ||
| 150 | FRF_CZ_RMFT_WILDCARD_MATCH, is_wild, | ||
| 151 | FRF_CZ_RMFT_DEST_MAC_HI, spec->data[2], | ||
| 152 | FRF_CZ_RMFT_DEST_MAC_LO, spec->data[1], | ||
| 153 | FRF_CZ_RMFT_VLAN_ID, spec->data[0]); | ||
| 154 | data3 = is_wild; | ||
| 155 | break; | ||
| 156 | } | ||
| 157 | |||
| 158 | default: | ||
| 159 | BUG(); | ||
| 160 | } | ||
| 161 | |||
| 162 | return spec->data[0] ^ spec->data[1] ^ spec->data[2] ^ data3; | ||
| 163 | } | ||
| 164 | |||
| 165 | static bool efx_filter_equal(const struct efx_filter_spec *left, | ||
| 166 | const struct efx_filter_spec *right) | ||
| 167 | { | ||
| 168 | if (left->type != right->type || | ||
| 169 | memcmp(left->data, right->data, sizeof(left->data))) | ||
| 170 | return false; | ||
| 171 | |||
| 172 | return true; | ||
| 173 | } | ||
| 174 | |||
| 175 | static int efx_filter_search(struct efx_filter_table *table, | ||
| 176 | struct efx_filter_spec *spec, u32 key, | ||
| 177 | bool for_insert, int *depth_required) | ||
| 178 | { | ||
| 179 | unsigned hash, incr, filter_idx, depth; | ||
| 180 | struct efx_filter_spec *cmp; | ||
| 181 | |||
| 182 | hash = efx_filter_hash(key); | ||
| 183 | incr = efx_filter_increment(key); | ||
| 184 | |||
| 185 | for (depth = 1, filter_idx = hash & (table->size - 1); | ||
| 186 | test_bit(filter_idx, table->used_bitmap); | ||
| 187 | ++depth) { | ||
| 188 | cmp = &table->spec[filter_idx]; | ||
| 189 | if (efx_filter_equal(spec, cmp)) | ||
| 190 | goto found; | ||
| 191 | filter_idx = (filter_idx + incr) & (table->size - 1); | ||
| 192 | } | ||
| 193 | if (!for_insert) | ||
| 194 | return -ENOENT; | ||
| 195 | found: | ||
| 196 | *depth_required = depth; | ||
| 197 | return filter_idx; | ||
| 198 | } | ||
| 199 | |||
| 200 | /** | ||
| 201 | * efx_filter_insert_filter - add or replace a filter | ||
| 202 | * @efx: NIC in which to insert the filter | ||
| 203 | * @spec: Specification for the filter | ||
| 204 | * @replace: Flag for whether the specified filter may replace a filter | ||
| 205 | * with an identical match expression and equal or lower priority | ||
| 206 | * | ||
| 207 | * On success, return the filter index within its table. | ||
| 208 | * On failure, return a negative error code. | ||
| 209 | */ | ||
| 210 | int efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, | ||
| 211 | bool replace) | ||
| 212 | { | ||
| 213 | struct efx_filter_state *state = efx->filter_state; | ||
| 214 | enum efx_filter_table_id table_id = | ||
| 215 | efx_filter_type_table_id(spec->type); | ||
| 216 | struct efx_filter_table *table = &state->table[table_id]; | ||
| 217 | struct efx_filter_spec *saved_spec; | ||
| 218 | efx_oword_t filter; | ||
| 219 | int filter_idx, depth; | ||
| 220 | u32 key; | ||
| 221 | int rc; | ||
| 222 | |||
| 223 | if (table->size == 0) | ||
| 224 | return -EINVAL; | ||
| 225 | |||
| 226 | key = efx_filter_build(&filter, spec); | ||
| 227 | |||
| 228 | netif_vdbg(efx, hw, efx->net_dev, | ||
| 229 | "%s: type %d search_depth=%d", __func__, spec->type, | ||
| 230 | state->search_depth[spec->type]); | ||
| 231 | |||
| 232 | spin_lock_bh(&state->lock); | ||
| 233 | |||
| 234 | rc = efx_filter_search(table, spec, key, true, &depth); | ||
| 235 | if (rc < 0) | ||
| 236 | goto out; | ||
| 237 | filter_idx = rc; | ||
| 238 | BUG_ON(filter_idx >= table->size); | ||
| 239 | saved_spec = &table->spec[filter_idx]; | ||
| 240 | |||
| 241 | if (test_bit(filter_idx, table->used_bitmap)) { | ||
| 242 | /* Should we replace the existing filter? */ | ||
| 243 | if (!replace) { | ||
| 244 | rc = -EEXIST; | ||
| 245 | goto out; | ||
| 246 | } | ||
| 247 | if (spec->priority < saved_spec->priority) { | ||
| 248 | rc = -EPERM; | ||
| 249 | goto out; | ||
| 250 | } | ||
| 251 | } else { | ||
| 252 | __set_bit(filter_idx, table->used_bitmap); | ||
| 253 | ++table->used; | ||
| 254 | } | ||
| 255 | *saved_spec = *spec; | ||
| 256 | |||
| 257 | if (state->search_depth[spec->type] < depth) { | ||
| 258 | state->search_depth[spec->type] = depth; | ||
| 259 | efx_filter_push_rx_limits(efx); | ||
| 260 | } | ||
| 261 | |||
| 262 | efx_writeo(efx, &filter, table->offset + table->step * filter_idx); | ||
| 263 | |||
| 264 | netif_vdbg(efx, hw, efx->net_dev, | ||
| 265 | "%s: filter type %d index %d rxq %u set", | ||
| 266 | __func__, spec->type, filter_idx, spec->dmaq_id); | ||
| 267 | |||
| 268 | out: | ||
| 269 | spin_unlock_bh(&state->lock); | ||
| 270 | return rc; | ||
| 271 | } | ||
| 272 | |||
| 273 | static void efx_filter_table_clear_entry(struct efx_nic *efx, | ||
| 274 | struct efx_filter_table *table, | ||
| 275 | int filter_idx) | ||
| 276 | { | ||
| 277 | static efx_oword_t filter; | ||
| 278 | |||
| 279 | if (test_bit(filter_idx, table->used_bitmap)) { | ||
| 280 | __clear_bit(filter_idx, table->used_bitmap); | ||
| 281 | --table->used; | ||
| 282 | memset(&table->spec[filter_idx], 0, sizeof(table->spec[0])); | ||
| 283 | |||
| 284 | efx_writeo(efx, &filter, | ||
| 285 | table->offset + table->step * filter_idx); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | /** | ||
| 290 | * efx_filter_remove_filter - remove a filter by specification | ||
| 291 | * @efx: NIC from which to remove the filter | ||
| 292 | * @spec: Specification for the filter | ||
| 293 | * | ||
| 294 | * On success, return zero. | ||
| 295 | * On failure, return a negative error code. | ||
| 296 | */ | ||
| 297 | int efx_filter_remove_filter(struct efx_nic *efx, struct efx_filter_spec *spec) | ||
| 298 | { | ||
| 299 | struct efx_filter_state *state = efx->filter_state; | ||
| 300 | enum efx_filter_table_id table_id = | ||
| 301 | efx_filter_type_table_id(spec->type); | ||
| 302 | struct efx_filter_table *table = &state->table[table_id]; | ||
| 303 | struct efx_filter_spec *saved_spec; | ||
| 304 | efx_oword_t filter; | ||
| 305 | int filter_idx, depth; | ||
| 306 | u32 key; | ||
| 307 | int rc; | ||
| 308 | |||
| 309 | key = efx_filter_build(&filter, spec); | ||
| 310 | |||
| 311 | spin_lock_bh(&state->lock); | ||
| 312 | |||
| 313 | rc = efx_filter_search(table, spec, key, false, &depth); | ||
| 314 | if (rc < 0) | ||
| 315 | goto out; | ||
| 316 | filter_idx = rc; | ||
| 317 | saved_spec = &table->spec[filter_idx]; | ||
| 318 | |||
| 319 | if (spec->priority < saved_spec->priority) { | ||
| 320 | rc = -EPERM; | ||
| 321 | goto out; | ||
| 322 | } | ||
| 323 | |||
| 324 | efx_filter_table_clear_entry(efx, table, filter_idx); | ||
| 325 | if (table->used == 0) | ||
| 326 | efx_filter_table_reset_search_depth(state, table_id); | ||
| 327 | rc = 0; | ||
| 328 | |||
| 329 | out: | ||
| 330 | spin_unlock_bh(&state->lock); | ||
| 331 | return rc; | ||
| 332 | } | ||
| 333 | |||
| 334 | /** | ||
| 335 | * efx_filter_table_clear - remove filters from a table by priority | ||
| 336 | * @efx: NIC from which to remove the filters | ||
| 337 | * @table_id: Table from which to remove the filters | ||
| 338 | * @priority: Maximum priority to remove | ||
| 339 | */ | ||
| 340 | void efx_filter_table_clear(struct efx_nic *efx, | ||
| 341 | enum efx_filter_table_id table_id, | ||
| 342 | enum efx_filter_priority priority) | ||
| 343 | { | ||
| 344 | struct efx_filter_state *state = efx->filter_state; | ||
| 345 | struct efx_filter_table *table = &state->table[table_id]; | ||
| 346 | int filter_idx; | ||
| 347 | |||
| 348 | spin_lock_bh(&state->lock); | ||
| 349 | |||
| 350 | for (filter_idx = 0; filter_idx < table->size; ++filter_idx) | ||
| 351 | if (table->spec[filter_idx].priority <= priority) | ||
| 352 | efx_filter_table_clear_entry(efx, table, filter_idx); | ||
| 353 | if (table->used == 0) | ||
| 354 | efx_filter_table_reset_search_depth(state, table_id); | ||
| 355 | |||
| 356 | spin_unlock_bh(&state->lock); | ||
| 357 | } | ||
| 358 | |||
| 359 | /* Restore filter stater after reset */ | ||
| 360 | void efx_restore_filters(struct efx_nic *efx) | ||
| 361 | { | ||
| 362 | struct efx_filter_state *state = efx->filter_state; | ||
| 363 | enum efx_filter_table_id table_id; | ||
| 364 | struct efx_filter_table *table; | ||
| 365 | efx_oword_t filter; | ||
| 366 | int filter_idx; | ||
| 367 | |||
| 368 | spin_lock_bh(&state->lock); | ||
| 369 | |||
| 370 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | ||
| 371 | table = &state->table[table_id]; | ||
| 372 | for (filter_idx = 0; filter_idx < table->size; filter_idx++) { | ||
| 373 | if (!test_bit(filter_idx, table->used_bitmap)) | ||
| 374 | continue; | ||
| 375 | efx_filter_build(&filter, &table->spec[filter_idx]); | ||
| 376 | efx_writeo(efx, &filter, | ||
| 377 | table->offset + table->step * filter_idx); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | efx_filter_push_rx_limits(efx); | ||
| 382 | |||
| 383 | spin_unlock_bh(&state->lock); | ||
| 384 | } | ||
| 385 | |||
| 386 | int efx_probe_filters(struct efx_nic *efx) | ||
| 387 | { | ||
| 388 | struct efx_filter_state *state; | ||
| 389 | struct efx_filter_table *table; | ||
| 390 | unsigned table_id; | ||
| 391 | |||
| 392 | state = kzalloc(sizeof(*efx->filter_state), GFP_KERNEL); | ||
| 393 | if (!state) | ||
| 394 | return -ENOMEM; | ||
| 395 | efx->filter_state = state; | ||
| 396 | |||
| 397 | spin_lock_init(&state->lock); | ||
| 398 | |||
| 399 | if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { | ||
| 400 | table = &state->table[EFX_FILTER_TABLE_RX_IP]; | ||
| 401 | table->offset = FR_BZ_RX_FILTER_TBL0; | ||
| 402 | table->size = FR_BZ_RX_FILTER_TBL0_ROWS; | ||
| 403 | table->step = FR_BZ_RX_FILTER_TBL0_STEP; | ||
| 404 | } | ||
| 405 | |||
| 406 | if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { | ||
| 407 | table = &state->table[EFX_FILTER_TABLE_RX_MAC]; | ||
| 408 | table->offset = FR_CZ_RX_MAC_FILTER_TBL0; | ||
| 409 | table->size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS; | ||
| 410 | table->step = FR_CZ_RX_MAC_FILTER_TBL0_STEP; | ||
| 411 | } | ||
| 412 | |||
| 413 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | ||
| 414 | table = &state->table[table_id]; | ||
| 415 | if (table->size == 0) | ||
| 416 | continue; | ||
| 417 | table->used_bitmap = kcalloc(BITS_TO_LONGS(table->size), | ||
| 418 | sizeof(unsigned long), | ||
| 419 | GFP_KERNEL); | ||
| 420 | if (!table->used_bitmap) | ||
| 421 | goto fail; | ||
| 422 | table->spec = vmalloc(table->size * sizeof(*table->spec)); | ||
| 423 | if (!table->spec) | ||
| 424 | goto fail; | ||
| 425 | memset(table->spec, 0, table->size * sizeof(*table->spec)); | ||
| 426 | } | ||
| 427 | |||
| 428 | return 0; | ||
| 429 | |||
| 430 | fail: | ||
| 431 | efx_remove_filters(efx); | ||
| 432 | return -ENOMEM; | ||
| 433 | } | ||
| 434 | |||
| 435 | void efx_remove_filters(struct efx_nic *efx) | ||
| 436 | { | ||
| 437 | struct efx_filter_state *state = efx->filter_state; | ||
| 438 | enum efx_filter_table_id table_id; | ||
| 439 | |||
| 440 | for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) { | ||
| 441 | kfree(state->table[table_id].used_bitmap); | ||
| 442 | vfree(state->table[table_id].spec); | ||
| 443 | } | ||
| 444 | kfree(state); | ||
| 445 | } | ||
diff --git a/drivers/net/sfc/filter.h b/drivers/net/sfc/filter.h new file mode 100644 index 000000000000..a53319ded79c --- /dev/null +++ b/drivers/net/sfc/filter.h | |||
| @@ -0,0 +1,189 @@ | |||
| 1 | /**************************************************************************** | ||
| 2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
| 3 | * Copyright 2005-2010 Solarflare Communications Inc. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published | ||
| 7 | * by the Free Software Foundation, incorporated herein by reference. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #ifndef EFX_FILTER_H | ||
| 11 | #define EFX_FILTER_H | ||
| 12 | |||
| 13 | #include <linux/types.h> | ||
| 14 | |||
| 15 | enum efx_filter_table_id { | ||
| 16 | EFX_FILTER_TABLE_RX_IP = 0, | ||
| 17 | EFX_FILTER_TABLE_RX_MAC, | ||
| 18 | EFX_FILTER_TABLE_COUNT, | ||
| 19 | }; | ||
| 20 | |||
| 21 | /** | ||
| 22 | * enum efx_filter_type - type of hardware filter | ||
| 23 | * @EFX_FILTER_RX_TCP_FULL: RX, matching TCP/IPv4 4-tuple | ||
| 24 | * @EFX_FILTER_RX_TCP_WILD: RX, matching TCP/IPv4 destination (host, port) | ||
| 25 | * @EFX_FILTER_RX_UDP_FULL: RX, matching UDP/IPv4 4-tuple | ||
| 26 | * @EFX_FILTER_RX_UDP_WILD: RX, matching UDP/IPv4 destination (host, port) | ||
| 27 | * @EFX_FILTER_RX_MAC_FULL: RX, matching Ethernet destination MAC address, VID | ||
| 28 | * @EFX_FILTER_RX_MAC_WILD: RX, matching Ethernet destination MAC address | ||
| 29 | * | ||
| 30 | * Falcon NICs only support the RX TCP/IPv4 and UDP/IPv4 filter types. | ||
| 31 | */ | ||
| 32 | enum efx_filter_type { | ||
| 33 | EFX_FILTER_RX_TCP_FULL = 0, | ||
| 34 | EFX_FILTER_RX_TCP_WILD, | ||
| 35 | EFX_FILTER_RX_UDP_FULL, | ||
| 36 | EFX_FILTER_RX_UDP_WILD, | ||
| 37 | EFX_FILTER_RX_MAC_FULL = 4, | ||
| 38 | EFX_FILTER_RX_MAC_WILD, | ||
| 39 | EFX_FILTER_TYPE_COUNT, | ||
| 40 | }; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * enum efx_filter_priority - priority of a hardware filter specification | ||
| 44 | * @EFX_FILTER_PRI_HINT: Performance hint | ||
| 45 | * @EFX_FILTER_PRI_MANUAL: Manually configured filter | ||
| 46 | * @EFX_FILTER_PRI_REQUIRED: Required for correct behaviour | ||
| 47 | */ | ||
| 48 | enum efx_filter_priority { | ||
| 49 | EFX_FILTER_PRI_HINT = 0, | ||
| 50 | EFX_FILTER_PRI_MANUAL, | ||
| 51 | EFX_FILTER_PRI_REQUIRED, | ||
| 52 | }; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * enum efx_filter_flags - flags for hardware filter specifications | ||
| 56 | * @EFX_FILTER_FLAG_RX_RSS: Use RSS to spread across multiple queues. | ||
| 57 | * By default, matching packets will be delivered only to the | ||
| 58 | * specified queue. If this flag is set, they will be delivered | ||
| 59 | * to a range of queues offset from the specified queue number | ||
| 60 | * according to the indirection table. | ||
| 61 | * @EFX_FILTER_FLAG_RX_SCATTER: Enable DMA scatter on the receiving | ||
| 62 | * queue. | ||
| 63 | * @EFX_FILTER_FLAG_RX_OVERRIDE_IP: Enables a MAC filter to override | ||
| 64 | * any IP filter that matches the same packet. By default, IP | ||
| 65 | * filters take precedence. | ||
| 66 | * | ||
| 67 | * Currently, no flags are defined for TX filters. | ||
| 68 | */ | ||
| 69 | enum efx_filter_flags { | ||
| 70 | EFX_FILTER_FLAG_RX_RSS = 0x01, | ||
| 71 | EFX_FILTER_FLAG_RX_SCATTER = 0x02, | ||
| 72 | EFX_FILTER_FLAG_RX_OVERRIDE_IP = 0x04, | ||
| 73 | }; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * struct efx_filter_spec - specification for a hardware filter | ||
| 77 | * @type: Type of match to be performed, from &enum efx_filter_type | ||
| 78 | * @priority: Priority of the filter, from &enum efx_filter_priority | ||
| 79 | * @flags: Miscellaneous flags, from &enum efx_filter_flags | ||
| 80 | * @dmaq_id: Source/target queue index | ||
| 81 | * @data: Match data (type-dependent) | ||
| 82 | * | ||
| 83 | * Use the efx_filter_set_*() functions to initialise the @type and | ||
| 84 | * @data fields. | ||
| 85 | */ | ||
| 86 | struct efx_filter_spec { | ||
| 87 | u8 type:4; | ||
| 88 | u8 priority:4; | ||
| 89 | u8 flags; | ||
| 90 | u16 dmaq_id; | ||
| 91 | u32 data[3]; | ||
| 92 | }; | ||
| 93 | |||
| 94 | /** | ||
| 95 | * efx_filter_set_rx_tcp_full - specify RX filter with TCP/IPv4 full match | ||
| 96 | * @spec: Specification to initialise | ||
| 97 | * @shost: Source host address (host byte order) | ||
| 98 | * @sport: Source port (host byte order) | ||
| 99 | * @dhost: Destination host address (host byte order) | ||
| 100 | * @dport: Destination port (host byte order) | ||
| 101 | */ | ||
| 102 | static inline void | ||
| 103 | efx_filter_set_rx_tcp_full(struct efx_filter_spec *spec, | ||
| 104 | u32 shost, u16 sport, u32 dhost, u16 dport) | ||
| 105 | { | ||
| 106 | spec->type = EFX_FILTER_RX_TCP_FULL; | ||
| 107 | spec->data[0] = sport | shost << 16; | ||
| 108 | spec->data[1] = dport << 16 | shost >> 16; | ||
| 109 | spec->data[2] = dhost; | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * efx_filter_set_rx_tcp_wild - specify RX filter with TCP/IPv4 wildcard match | ||
| 114 | * @spec: Specification to initialise | ||
| 115 | * @dhost: Destination host address (host byte order) | ||
| 116 | * @dport: Destination port (host byte order) | ||
| 117 | */ | ||
| 118 | static inline void | ||
| 119 | efx_filter_set_rx_tcp_wild(struct efx_filter_spec *spec, u32 dhost, u16 dport) | ||
| 120 | { | ||
| 121 | spec->type = EFX_FILTER_RX_TCP_WILD; | ||
| 122 | spec->data[0] = 0; | ||
| 123 | spec->data[1] = dport << 16; | ||
| 124 | spec->data[2] = dhost; | ||
| 125 | } | ||
| 126 | |||
| 127 | /** | ||
| 128 | * efx_filter_set_rx_udp_full - specify RX filter with UDP/IPv4 full match | ||
| 129 | * @spec: Specification to initialise | ||
| 130 | * @shost: Source host address (host byte order) | ||
| 131 | * @sport: Source port (host byte order) | ||
| 132 | * @dhost: Destination host address (host byte order) | ||
| 133 | * @dport: Destination port (host byte order) | ||
| 134 | */ | ||
| 135 | static inline void | ||
| 136 | efx_filter_set_rx_udp_full(struct efx_filter_spec *spec, | ||
| 137 | u32 shost, u16 sport, u32 dhost, u16 dport) | ||
| 138 | { | ||
| 139 | spec->type = EFX_FILTER_RX_UDP_FULL; | ||
| 140 | spec->data[0] = sport | shost << 16; | ||
| 141 | spec->data[1] = dport << 16 | shost >> 16; | ||
| 142 | spec->data[2] = dhost; | ||
| 143 | } | ||
| 144 | |||
| 145 | /** | ||
| 146 | * efx_filter_set_rx_udp_wild - specify RX filter with UDP/IPv4 wildcard match | ||
| 147 | * @spec: Specification to initialise | ||
| 148 | * @dhost: Destination host address (host byte order) | ||
| 149 | * @dport: Destination port (host byte order) | ||
| 150 | */ | ||
| 151 | static inline void | ||
| 152 | efx_filter_set_rx_udp_wild(struct efx_filter_spec *spec, u32 dhost, u16 dport) | ||
| 153 | { | ||
| 154 | spec->type = EFX_FILTER_RX_UDP_WILD; | ||
| 155 | spec->data[0] = dport; | ||
| 156 | spec->data[1] = 0; | ||
| 157 | spec->data[2] = dhost; | ||
| 158 | } | ||
| 159 | |||
| 160 | /** | ||
| 161 | * efx_filter_set_rx_mac_full - specify RX filter with MAC full match | ||
| 162 | * @spec: Specification to initialise | ||
| 163 | * @vid: VLAN ID | ||
| 164 | * @addr: Destination MAC address | ||
| 165 | */ | ||
| 166 | static inline void efx_filter_set_rx_mac_full(struct efx_filter_spec *spec, | ||
| 167 | u16 vid, const u8 *addr) | ||
| 168 | { | ||
| 169 | spec->type = EFX_FILTER_RX_MAC_FULL; | ||
| 170 | spec->data[0] = vid; | ||
| 171 | spec->data[1] = addr[2] << 24 | addr[3] << 16 | addr[4] << 8 | addr[5]; | ||
| 172 | spec->data[2] = addr[0] << 8 | addr[1]; | ||
| 173 | } | ||
| 174 | |||
| 175 | /** | ||
| 176 | * efx_filter_set_rx_mac_full - specify RX filter with MAC wildcard match | ||
| 177 | * @spec: Specification to initialise | ||
| 178 | * @addr: Destination MAC address | ||
| 179 | */ | ||
| 180 | static inline void efx_filter_set_rx_mac_wild(struct efx_filter_spec *spec, | ||
| 181 | const u8 *addr) | ||
| 182 | { | ||
| 183 | spec->type = EFX_FILTER_RX_MAC_WILD; | ||
| 184 | spec->data[0] = 0; | ||
| 185 | spec->data[1] = addr[2] << 24 | addr[3] << 16 | addr[4] << 8 | addr[5]; | ||
| 186 | spec->data[2] = addr[0] << 8 | addr[1]; | ||
| 187 | } | ||
| 188 | |||
| 189 | #endif /* EFX_FILTER_H */ | ||
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 152342dbff29..ea6691c83fba 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h | |||
| @@ -619,6 +619,8 @@ union efx_multicast_hash { | |||
| 619 | efx_oword_t oword[EFX_MCAST_HASH_ENTRIES / sizeof(efx_oword_t) / 8]; | 619 | efx_oword_t oword[EFX_MCAST_HASH_ENTRIES / sizeof(efx_oword_t) / 8]; |
| 620 | }; | 620 | }; |
| 621 | 621 | ||
| 622 | struct efx_filter_state; | ||
| 623 | |||
| 622 | /** | 624 | /** |
| 623 | * struct efx_nic - an Efx NIC | 625 | * struct efx_nic - an Efx NIC |
| 624 | * @name: Device name (net device name or bus id before net device registered) | 626 | * @name: Device name (net device name or bus id before net device registered) |
| @@ -799,6 +801,8 @@ struct efx_nic { | |||
| 799 | u64 loopback_modes; | 801 | u64 loopback_modes; |
| 800 | 802 | ||
| 801 | void *loopback_selftest; | 803 | void *loopback_selftest; |
| 804 | |||
| 805 | struct efx_filter_state *filter_state; | ||
| 802 | }; | 806 | }; |
| 803 | 807 | ||
| 804 | static inline int efx_dev_registered(struct efx_nic *efx) | 808 | static inline int efx_dev_registered(struct efx_nic *efx) |
diff --git a/drivers/net/sfc/regs.h b/drivers/net/sfc/regs.h index 18a3be428348..96430ed81c36 100644 --- a/drivers/net/sfc/regs.h +++ b/drivers/net/sfc/regs.h | |||
| @@ -2893,6 +2893,20 @@ | |||
| 2893 | #define FRF_AB_XX_FORCE_SIG_WIDTH 8 | 2893 | #define FRF_AB_XX_FORCE_SIG_WIDTH 8 |
| 2894 | #define FFE_AB_XX_FORCE_SIG_ALL_LANES 0xff | 2894 | #define FFE_AB_XX_FORCE_SIG_ALL_LANES 0xff |
| 2895 | 2895 | ||
| 2896 | /* RX_MAC_FILTER_TBL0 */ | ||
| 2897 | /* RMFT_DEST_MAC is wider than 32 bits */ | ||
| 2898 | #define FRF_CZ_RMFT_DEST_MAC_LO_LBN 12 | ||
| 2899 | #define FRF_CZ_RMFT_DEST_MAC_LO_WIDTH 32 | ||
| 2900 | #define FRF_CZ_RMFT_DEST_MAC_HI_LBN 44 | ||
| 2901 | #define FRF_CZ_RMFT_DEST_MAC_HI_WIDTH 16 | ||
| 2902 | |||
| 2903 | /* TX_MAC_FILTER_TBL0 */ | ||
| 2904 | /* TMFT_SRC_MAC is wider than 32 bits */ | ||
| 2905 | #define FRF_CZ_TMFT_SRC_MAC_LO_LBN 12 | ||
| 2906 | #define FRF_CZ_TMFT_SRC_MAC_LO_WIDTH 32 | ||
| 2907 | #define FRF_CZ_TMFT_SRC_MAC_HI_LBN 44 | ||
| 2908 | #define FRF_CZ_TMFT_SRC_MAC_HI_WIDTH 16 | ||
| 2909 | |||
| 2896 | /* DRIVER_EV */ | 2910 | /* DRIVER_EV */ |
| 2897 | /* Sub-fields of an RX flush completion event */ | 2911 | /* Sub-fields of an RX flush completion event */ |
| 2898 | #define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_LBN 12 | 2912 | #define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_LBN 12 |
