|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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)
|
|
|
{
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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)
|
|
|
{
|