ena: Implement full RSS reconfiguration

Bind RX/TX queues and MSI-X vectors to matching CPUs based on the RSS
bucket entries.

Introduce sysctls for the following RSS functionality:
- rss.indir_table:      indirection table mapping
- rss.indir_table_size: indirection table size
- rss.key:              RSS hash key (if Toeplitz used)

Said sysctls are only available when compiled without `option RSS`, as
kernel-side RSS support currently doesn't offer RSS reconfiguration.

Migrate the hash algorithm from CRC32 to Toeplitz and change the initial
hash value to 0x0 in order to match the standard Toeplitz implementation.
Provide helpers for hash key inversion required for HW operations.

Obtained from: Semihalf
MFC after: 2 weeks
Sponsored by: Amazon, Inc.
This commit is contained in:
Artur Rojek 2021-08-12 10:34:29 +02:00 committed by Marcin Wojtas
parent 223c8cb12e
commit 6d1ef2abd3
7 changed files with 525 additions and 20 deletions

View File

@ -269,6 +269,57 @@ command should be used:
.Bd -literal -offset indent
sysctl dev.ena.1.eni_metrics.sample_interval=10
.Ed
.It Va dev.ena.X.rss.indir_table_size
RSS indirection table size.
The default is 128.
Returns the number of entries in the RSS indirection table.
.Pp
Example:
To read the RSS indirection table size, the following command should be used:
.Bd -literal -offset indent
sysctl dev.ena.0.rss.indir_table_size
.Ed
.It Va dev.ena.X.rss.indir_table
RSS indirection table mapping.
The default is x:y key-pairs of indir_table_size length.
Updates selected indices of the RSS indirection table.
.Pp
The entry string consists of one or more x:y keypairs, where x stands for
the table index and y for its new value. Table indices that don't need to be
updated can be omitted from the string and will retain their existing values.
.Pp
If an index is entered more than once, the last value is used.
.Pp
Example:
To update two selected indices in the RSS indirection table, e.g. setting index
0 to queue 5 and then index 5 to queue 0, the following command should be used:
.Bd -literal -offset indent
sysctl dev.ena.0.rss.indir_table="0:5 5:0"
.Ed
.It Va dev.ena.X.rss.key
RSS hash key.
The default is 40 bytes long randomly generated hash key.
Controls the RSS Toeplitz hash algorithm key value.
.Pp
Only available when driver compiled without the kernel side RSS support.
.Pp
Example:
To change the RSS hash key value to
.Pp
0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
.br
0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
.br
0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
.br
0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
.br
0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
.Pp
the following command should be used:
.Bd -literal -offset indent
sysctl dev.ena.0.rss.key=6d5a56da255b0ec24167253d43a38fb0d0ca2bcbae7b30b477cb2da38030f20c6a42b73bbeac01fa
.Ed
.El
.Sh DIAGNOSTICS
.Ss Device initialization phase

View File

@ -601,8 +601,10 @@ static int
ena_setup_tx_resources(struct ena_adapter *adapter, int qid)
{
device_t pdev = adapter->pdev;
char thread_name[MAXCOMLEN + 1];
struct ena_que *que = &adapter->que[qid];
struct ena_ring *tx_ring = que->tx_ring;
cpuset_t *cpu_mask = NULL;
int size, i, err;
#ifdef DEV_NETMAP
bus_dmamap_t *map;
@ -686,8 +688,16 @@ ena_setup_tx_resources(struct ena_adapter *adapter, int qid)
tx_ring->running = true;
taskqueue_start_threads(&tx_ring->enqueue_tq, 1, PI_NET,
"%s txeq %d", device_get_nameunit(adapter->pdev), que->cpu);
#ifdef RSS
cpu_mask = &que->cpu_mask;
snprintf(thread_name, sizeof(thread_name), "%s txeq %d",
device_get_nameunit(adapter->pdev), que->cpu);
#else
snprintf(thread_name, sizeof(thread_name), "%s txeq %d",
device_get_nameunit(adapter->pdev), que->id);
#endif
taskqueue_start_threads_cpuset(&tx_ring->enqueue_tq, 1, PI_NET,
cpu_mask, "%s", thread_name);
return (0);
@ -1423,6 +1433,7 @@ ena_create_io_queues(struct ena_adapter *adapter)
struct ena_que *queue;
uint16_t ena_qid;
uint32_t msix_vector;
cpuset_t *cpu_mask = NULL;
int rc, i;
/* Create TX queues */
@ -1489,7 +1500,11 @@ ena_create_io_queues(struct ena_adapter *adapter)
queue->cleanup_tq = taskqueue_create_fast("ena cleanup",
M_WAITOK, taskqueue_thread_enqueue, &queue->cleanup_tq);
taskqueue_start_threads(&queue->cleanup_tq, 1, PI_NET,
#ifdef RSS
cpu_mask = &queue->cpu_mask;
#endif
taskqueue_start_threads_cpuset(&queue->cleanup_tq, 1, PI_NET,
cpu_mask,
"%s queue %d cleanup",
device_get_nameunit(adapter->pdev), i);
}
@ -1628,7 +1643,10 @@ ena_setup_mgmnt_intr(struct ena_adapter *adapter)
static int
ena_setup_io_intr(struct ena_adapter *adapter)
{
static int last_bind_cpu = -1;
#ifdef RSS
int num_buckets = rss_getnumbuckets();
static int last_bind = 0;
#endif
int irq_idx;
if (adapter->msix_entries == NULL)
@ -1646,15 +1664,12 @@ ena_setup_io_intr(struct ena_adapter *adapter)
ena_log(adapter->pdev, DBG, "ena_setup_io_intr vector: %d\n",
adapter->msix_entries[irq_idx].vector);
/*
* We want to bind rings to the corresponding cpu
* using something similar to the RSS round-robin technique.
*/
if (unlikely(last_bind_cpu < 0))
last_bind_cpu = CPU_FIRST();
#ifdef RSS
adapter->que[i].cpu = adapter->irq_tbl[irq_idx].cpu =
last_bind_cpu;
last_bind_cpu = CPU_NEXT(last_bind_cpu);
rss_getcpu(last_bind);
last_bind = (last_bind + 1) % num_buckets;
CPU_SETOF(adapter->que[i].cpu, &adapter->que[i].cpu_mask);
#endif
}
return (0);
@ -1746,6 +1761,19 @@ ena_request_io_irq(struct ena_adapter *adapter)
goto err;
}
irq->requested = true;
#ifdef RSS
rc = bus_bind_intr(adapter->pdev, irq->res, irq->cpu);
if (unlikely(rc != 0)) {
ena_log(pdev, ERR, "failed to bind "
"interrupt handler for irq %ju to cpu %d: %d\n",
rman_get_start(irq->res), irq->cpu, rc);
goto err;
}
ena_log(pdev, INFO, "queue %d - cpu %d\n",
i - ENA_IO_IRQ_FIRST_IDX, irq->cpu);
#endif
}
return (rc);
@ -2464,6 +2492,10 @@ ena_calc_max_io_queue_num(device_t pdev, struct ena_com_dev *ena_dev,
/* 1 IRQ for for mgmnt and 1 IRQ for each TX/RX pair */
max_num_io_queues = min_t(uint32_t, max_num_io_queues,
pci_msix_count(pdev) - 1);
#ifdef RSS
max_num_io_queues = min_t(uint32_t, max_num_io_queues,
rss_getnumbuckets());
#endif
return (max_num_io_queues);
}
@ -2692,7 +2724,8 @@ ena_config_host_info(struct ena_com_dev *ena_dev, device_t dev)
(DRV_MODULE_VER_SUBMINOR << ENA_ADMIN_HOST_INFO_SUB_MINOR_SHIFT);
host_info->num_cpus = mp_ncpus;
host_info->driver_supported_features =
ENA_ADMIN_HOST_INFO_RX_OFFSET_MASK;
ENA_ADMIN_HOST_INFO_RX_OFFSET_MASK |
ENA_ADMIN_HOST_INFO_RSS_CONFIGURABLE_FUNCTION_KEY_MASK;
rc = ena_com_set_host_attributes(ena_dev);
if (unlikely(rc != 0)) {
@ -3709,6 +3742,9 @@ ena_detach(device_t pdev)
ena_free_pci_resources(adapter);
if (adapter->rss_indir != NULL)
free(adapter->rss_indir, M_DEVBUF);
if (likely(ENA_FLAG_ISSET(ENA_FLAG_RSS_ACTIVE, adapter)))
ena_com_rss_destroy(ena_dev);

View File

@ -34,6 +34,7 @@
#ifndef ENA_H
#define ENA_H
#include "opt_rss.h"
#include "ena-com/ena_com.h"
#include "ena-com/ena_eth_com.h"
@ -122,6 +123,8 @@
#define ENA_IO_TXQ_IDX(q) (2 * (q))
#define ENA_IO_RXQ_IDX(q) (2 * (q) + 1)
#define ENA_IO_TXQ_IDX_TO_COMBINED_IDX(q) ((q) / 2)
#define ENA_IO_RXQ_IDX_TO_COMBINED_IDX(q) (((q) - 1) / 2)
#define ENA_MGMNT_IRQ_IDX 0
#define ENA_IO_IRQ_FIRST_IDX 1
@ -200,7 +203,9 @@ struct ena_irq {
void *cookie;
unsigned int vector;
bool requested;
#ifdef RSS
int cpu;
#endif
char name[ENA_IRQNAME_SIZE];
};
@ -213,7 +218,10 @@ struct ena_que {
struct taskqueue *cleanup_tq;
uint32_t id;
#ifdef RSS
int cpu;
cpuset_t cpu_mask;
#endif
struct sysctl_oid *oid;
};
@ -431,7 +439,7 @@ struct ena_adapter {
uint32_t buf_ring_size;
/* RSS*/
uint8_t rss_ind_tbl[ENA_RX_RSS_TABLE_SIZE];
struct ena_indir *rss_indir;
uint8_t mac_addr[ETHER_ADDR_LEN];
/* mdio and phy*/

View File

@ -36,6 +36,9 @@ __FBSDID("$FreeBSD$");
#ifdef DEV_NETMAP
#include "ena_netmap.h"
#endif /* DEV_NETMAP */
#ifdef RSS
#include <net/rss_config.h>
#endif /* RSS */
/*********************************************************************
* Static functions prototypes
@ -129,6 +132,9 @@ ena_mq_start(if_t ifp, struct mbuf *m)
struct ena_ring *tx_ring;
int ret, is_drbr_empty;
uint32_t i;
#ifdef RSS
uint32_t bucket_id;
#endif
if (unlikely((if_getdrvflags(adapter->ifp) & IFF_DRV_RUNNING) == 0))
return (ENODEV);
@ -140,7 +146,13 @@ ena_mq_start(if_t ifp, struct mbuf *m)
* It should improve performance.
*/
if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
i = m->m_pkthdr.flowid % adapter->num_io_queues;
#ifdef RSS
if (rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m),
&bucket_id) == 0)
i = bucket_id % adapter->num_io_queues;
else
#endif
i = m->m_pkthdr.flowid % adapter->num_io_queues;
} else {
i = curcpu % adapter->num_io_queues;
}

View File

@ -59,6 +59,45 @@ ena_rss_key_fill(void *key, size_t size)
memcpy(key, default_key, size);
}
/*
* ENA HW expects the key to be in reverse-byte order.
*/
static void
ena_rss_reorder_hash_key(u8 *reordered_key, const u8 *key, size_t key_size)
{
int i;
key = key + key_size - 1;
for (i = 0; i < key_size; ++i)
*reordered_key++ = *key--;
}
int ena_rss_set_hash(struct ena_com_dev *ena_dev, const u8 *key)
{
enum ena_admin_hash_functions ena_func = ENA_ADMIN_TOEPLITZ;
u8 hw_key[ENA_HASH_KEY_SIZE];
ena_rss_reorder_hash_key(hw_key, key, ENA_HASH_KEY_SIZE);
return (ena_com_fill_hash_function(ena_dev, ena_func, hw_key,
ENA_HASH_KEY_SIZE, 0x0));
}
int ena_rss_get_hash_key(struct ena_com_dev *ena_dev, u8 *key)
{
u8 hw_key[ENA_HASH_KEY_SIZE];
int rc;
rc = ena_com_get_hash_key(ena_dev, hw_key);
if (rc != 0)
return rc;
ena_rss_reorder_hash_key(key, hw_key, ENA_HASH_KEY_SIZE);
return (0);
}
static int
ena_rss_init_default(struct ena_adapter *adapter)
{
@ -73,7 +112,11 @@ ena_rss_init_default(struct ena_adapter *adapter)
}
for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; i++) {
#ifdef RSS
qid = rss_get_indirection_to_bucket(i) % adapter->num_io_queues;
#else
qid = i % adapter->num_io_queues;
#endif
rc = ena_com_indirect_table_fill_entry(ena_dev, i,
ENA_IO_RXQ_IDX(qid));
if (unlikely((rc != 0) && (rc != EOPNOTSUPP))) {
@ -89,12 +132,11 @@ ena_rss_init_default(struct ena_adapter *adapter)
uint8_t hash_key[RSS_KEYSIZE];
rss_getkey(hash_key);
rc = ena_com_fill_hash_function(ena_dev, ENA_ADMIN_TOEPLITZ,
hash_key, RSS_KEYSIZE, 0xFFFFFFFF);
rc = ena_rss_set_hash(ena_dev, hash_key);
} else
#endif
rc = ena_com_fill_hash_function(ena_dev, ENA_ADMIN_CRC32, NULL,
ENA_HASH_KEY_SIZE, 0xFFFFFFFF);
rc = ena_com_fill_hash_function(ena_dev, ENA_ADMIN_TOEPLITZ, NULL,
ENA_HASH_KEY_SIZE, 0x0);
if (unlikely((rc != 0) && (rc != EOPNOTSUPP))) {
ena_log(dev, ERR, "Cannot fill hash function\n");
goto err_rss_destroy;
@ -106,7 +148,9 @@ ena_rss_init_default(struct ena_adapter *adapter)
goto err_rss_destroy;
}
return (0);
rc = ena_rss_indir_init(adapter);
return (rc == EOPNOTSUPP ? 0 : rc);
err_rss_destroy:
ena_com_rss_destroy(ena_dev);
@ -180,3 +224,77 @@ ena_rss_init_default_deferred(void *arg)
}
}
SYSINIT(ena_rss_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_SECOND, ena_rss_init_default_deferred, NULL);
int
ena_rss_indir_get(struct ena_adapter *adapter, uint32_t *table)
{
int rc, i;
rc = ena_com_indirect_table_get(adapter->ena_dev, table);
if (rc != 0) {
if (rc == EOPNOTSUPP)
device_printf(adapter->pdev,
"Reading from indirection table not supported\n");
else
device_printf(adapter->pdev,
"Unable to get indirection table\n");
return (rc);
}
for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; ++i)
table[i] = ENA_IO_RXQ_IDX_TO_COMBINED_IDX(table[i]);
return (0);
}
int
ena_rss_indir_set(struct ena_adapter *adapter, uint32_t *table)
{
int rc, i;
for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; ++i) {
rc = ena_com_indirect_table_fill_entry(adapter->ena_dev, i,
ENA_IO_RXQ_IDX(table[i]));
if (rc != 0) {
device_printf(adapter->pdev,
"Cannot fill indirection table entry %d\n", i);
return (rc);
}
}
rc = ena_com_indirect_table_set(adapter->ena_dev);
if (rc == EOPNOTSUPP)
device_printf(adapter->pdev,
"Writing to indirection table not supported\n");
else if (rc != 0)
device_printf(adapter->pdev,
"Cannot set indirection table\n");
return (rc);
}
int
ena_rss_indir_init(struct ena_adapter *adapter)
{
struct ena_indir *indir = adapter->rss_indir;
int rc;
if (indir == NULL) {
adapter->rss_indir = indir = malloc(sizeof(struct ena_indir),
M_DEVBUF, M_WAITOK | M_ZERO);
if (indir == NULL)
return (ENOMEM);
}
rc = ena_rss_indir_get(adapter, indir->table);
if (rc != 0) {
free(adapter->rss_indir, M_DEVBUF);
adapter->rss_indir = NULL;
return (rc);
}
ena_rss_copy_indir_buf(indir->sysctl_buf, indir->table);
return (0);
}

View File

@ -44,6 +44,30 @@
#include "ena.h"
#define ENA_RX_RSS_MSG_RECORD_SZ 8
struct ena_indir {
uint32_t table[ENA_RX_RSS_TABLE_SIZE];
/* This is the buffer wired to `rss.indir_table` sysctl. */
char sysctl_buf[ENA_RX_RSS_TABLE_SIZE * ENA_RX_RSS_MSG_RECORD_SZ];
};
int ena_rss_set_hash(struct ena_com_dev *ena_dev, const u8 *key);
int ena_rss_get_hash_key(struct ena_com_dev *ena_dev, u8 *key);
int ena_rss_configure(struct ena_adapter *);
int ena_rss_indir_get(struct ena_adapter *adapter, uint32_t *table);
int ena_rss_indir_set(struct ena_adapter *adapter, uint32_t *table);
int ena_rss_indir_init(struct ena_adapter *adapter);
static inline void
ena_rss_copy_indir_buf(char *buf, uint32_t *table)
{
int i;
for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; ++i) {
buf += snprintf(buf, ENA_RX_RSS_MSG_RECORD_SZ + 1,
"%s%d:%d", i == 0 ? "" : " ", i, table[i]);
}
}
#endif /* !(ENA_RSS_H) */

View File

@ -31,19 +31,31 @@
#include <sys/param.h>
__FBSDID("$FreeBSD$");
#include "opt_rss.h"
#include "ena_sysctl.h"
#include "ena_rss.h"
static void ena_sysctl_add_wd(struct ena_adapter *);
static void ena_sysctl_add_stats(struct ena_adapter *);
static void ena_sysctl_add_eni_metrics(struct ena_adapter *);
static void ena_sysctl_add_tuneables(struct ena_adapter *);
/* Kernel option RSS prevents manipulation of key hash and indirection table. */
#ifndef RSS
static void ena_sysctl_add_rss(struct ena_adapter *);
#endif
static int ena_sysctl_buf_ring_size(SYSCTL_HANDLER_ARGS);
static int ena_sysctl_rx_queue_size(SYSCTL_HANDLER_ARGS);
static int ena_sysctl_io_queues_nb(SYSCTL_HANDLER_ARGS);
static int ena_sysctl_eni_metrics_interval(SYSCTL_HANDLER_ARGS);
#ifndef RSS
static int ena_sysctl_rss_key(SYSCTL_HANDLER_ARGS);
static int ena_sysctl_rss_indir_table(SYSCTL_HANDLER_ARGS);
#endif
/* Limit max ENI sample rate to be an hour. */
#define ENI_METRICS_MAX_SAMPLE_INTERVAL 3600
#define ENA_HASH_KEY_MSG_SIZE (ENA_HASH_KEY_SIZE * 2 + 1)
static SYSCTL_NODE(_hw, OID_AUTO, ena, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"ENA driver parameters");
@ -83,6 +95,8 @@ SYSCTL_BOOL(_hw_ena, OID_AUTO, force_large_llq_header, CTLFLAG_RDTUN,
&ena_force_large_llq_header, 0,
"Increases maximum supported header size in LLQ mode to 224 bytes, while reducing the maximum Tx queue size by half.\n");
int ena_rss_table_size = ENA_RX_RSS_TABLE_SIZE;
void
ena_sysctl_add_nodes(struct ena_adapter *adapter)
{
@ -90,6 +104,9 @@ ena_sysctl_add_nodes(struct ena_adapter *adapter)
ena_sysctl_add_stats(adapter);
ena_sysctl_add_eni_metrics(adapter);
ena_sysctl_add_tuneables(adapter);
#ifndef RSS
ena_sysctl_add_rss(adapter);
#endif
}
static void
@ -405,6 +422,45 @@ ena_sysctl_add_tuneables(struct ena_adapter *adapter)
ena_sysctl_io_queues_nb, "I", "Number of IO queues.");
}
/* Kernel option RSS prevents manipulation of key hash and indirection table. */
#ifndef RSS
static void
ena_sysctl_add_rss(struct ena_adapter *adapter)
{
device_t dev;
struct sysctl_ctx_list *ctx;
struct sysctl_oid *tree;
struct sysctl_oid_list *child;
dev = adapter->pdev;
ctx = device_get_sysctl_ctx(dev);
tree = device_get_sysctl_tree(dev);
child = SYSCTL_CHILDREN(tree);
/* RSS options */
tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rss",
CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, "Receive Side Scaling options.");
child = SYSCTL_CHILDREN(tree);
/* RSS hash key */
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "key",
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, adapter, 0,
ena_sysctl_rss_key, "A", "RSS key.");
/* Tuneable RSS indirection table */
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "indir_table",
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, adapter, 0,
ena_sysctl_rss_indir_table, "A", "RSS indirection table.");
/* RSS indirection table size */
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "indir_table_size",
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, &ena_rss_table_size, 0,
"RSS indirection table size.");
}
#endif /* RSS */
/*
* ena_sysctl_update_queue_node_nb - Register/unregister sysctl queue nodes.
@ -662,3 +718,203 @@ ena_sysctl_eni_metrics_interval(SYSCTL_HANDLER_ARGS)
return (0);
}
#ifndef RSS
/*
* Change the Receive Side Scaling hash key.
*/
static int
ena_sysctl_rss_key(SYSCTL_HANDLER_ARGS)
{
struct ena_adapter *adapter = arg1;
struct ena_com_dev *ena_dev = adapter->ena_dev;
enum ena_admin_hash_functions ena_func;
char msg[ENA_HASH_KEY_MSG_SIZE];
char elem[3] = { 0 };
char *endp;
u8 rss_key[ENA_HASH_KEY_SIZE];
int error, i;
ENA_LOCK_LOCK();
if (unlikely(!ENA_FLAG_ISSET(ENA_FLAG_DEVICE_RUNNING, adapter))) {
error = EINVAL;
goto unlock;
}
if (unlikely(!ENA_FLAG_ISSET(ENA_FLAG_RSS_ACTIVE, adapter))) {
error = ENOTSUP;
goto unlock;
}
error = sysctl_wire_old_buffer(req, sizeof(msg));
if (error != 0)
goto unlock;
error = ena_com_get_hash_function(adapter->ena_dev, &ena_func);
if (error != 0) {
device_printf(adapter->pdev, "Cannot get hash function\n");
goto unlock;
}
if (ena_func != ENA_ADMIN_TOEPLITZ) {
error = EINVAL;
device_printf(adapter->pdev, "Unsupported hash algorithm\n");
goto unlock;
}
error = ena_rss_get_hash_key(ena_dev, rss_key);
if (error != 0) {
device_printf(adapter->pdev, "Cannot get hash key\n");
goto unlock;
}
for (i = 0; i < ENA_HASH_KEY_SIZE; ++i)
snprintf(&msg[i * 2], 3, "%02x", rss_key[i]);
error = sysctl_handle_string(oidp, msg, sizeof(msg), req);
if (error != 0 || req->newptr == NULL)
goto unlock;
if (strlen(msg) != sizeof(msg) - 1) {
error = EINVAL;
device_printf(adapter->pdev, "Invalid key size\n");
goto unlock;
}
for (i = 0; i < ENA_HASH_KEY_SIZE; ++i) {
strncpy(elem, &msg[i * 2], 2);
rss_key[i] = strtol(elem, &endp, 16);
/* Both hex nibbles in the string must be valid to continue. */
if (endp == elem || *endp != '\0' || rss_key[i] < 0) {
error = EINVAL;
device_printf(adapter->pdev,
"Invalid key hex value: '%c'\n", *endp);
goto unlock;
}
}
error = ena_rss_set_hash(ena_dev, rss_key);
if (error != 0)
device_printf(adapter->pdev, "Cannot fill hash key\n");
unlock:
ENA_LOCK_UNLOCK();
return (error);
}
/*
* Change the Receive Side Scaling indirection table.
*
* The sysctl entry string consists of one or more `x:y` keypairs, where
* x stands for the table index and y for its new value.
* Table indices that don't need to be updated can be omitted from the string
* and will retain their existing values. If an index is entered more than once,
* the last value is used.
*
* Example:
* To update two selected indices in the RSS indirection table, e.g. setting
* index 0 to queue 5 and then index 5 to queue 0, the below command should be
* used:
* sysctl dev.ena.0.rss.indir_table="0:5 5:0"
*/
static int
ena_sysctl_rss_indir_table(SYSCTL_HANDLER_ARGS)
{
int num_queues, error;
struct ena_adapter *adapter = arg1;
struct ena_com_dev *ena_dev;
struct ena_indir *indir;
char *msg, *buf, *endp;
uint32_t idx, value;
ENA_LOCK_LOCK();
if (unlikely(!ENA_FLAG_ISSET(ENA_FLAG_DEVICE_RUNNING, adapter))) {
error = EINVAL;
goto unlock;
}
if (unlikely(!ENA_FLAG_ISSET(ENA_FLAG_RSS_ACTIVE, adapter))) {
error = ENOTSUP;
goto unlock;
}
ena_dev = adapter->ena_dev;
indir = adapter->rss_indir;
msg = indir->sysctl_buf;
if (unlikely(indir == NULL)) {
error = ENOTSUP;
goto unlock;
}
error = sysctl_handle_string(oidp, msg, sizeof(indir->sysctl_buf), req);
if (error != 0 || req->newptr == NULL)
goto unlock;
num_queues = adapter->num_io_queues;
/*
* This sysctl expects msg to be a list of `x:y` record pairs,
* where x is the indirection table index and y is its value.
*/
for (buf = msg; *buf != '\0'; buf = endp) {
idx = strtol(buf, &endp, 10);
if (endp == buf || idx < 0) {
device_printf(adapter->pdev, "Invalid index: %s\n",
buf);
error = EINVAL;
break;
}
if (idx >= ENA_RX_RSS_TABLE_SIZE) {
device_printf(adapter->pdev, "Index %d out of range\n",
idx);
error = ERANGE;
break;
}
buf = endp;
if (*buf++ != ':') {
device_printf(adapter->pdev, "Missing ':' separator\n");
error = EINVAL;
break;
}
value = strtol(buf, &endp, 10);
if (endp == buf || value < 0) {
device_printf(adapter->pdev, "Invalid value: %s\n",
buf);
error = EINVAL;
break;
}
if (value >= num_queues) {
device_printf(adapter->pdev, "Value %d out of range\n",
value);
error = ERANGE;
break;
}
indir->table[idx] = value;
}
if (error != 0) /* Reload indirection table with last good data. */
ena_rss_indir_get(adapter, indir->table);
/* At this point msg has been clobbered by sysctl_handle_string. */
ena_rss_copy_indir_buf(msg, indir->table);
if (error == 0)
error = ena_rss_indir_set(adapter, indir->table);
unlock:
ENA_LOCK_UNLOCK();
return (error);
}
#endif /* RSS */