mirror of https://github.com/F-Stack/f-stack.git
419 lines
9.5 KiB
C
419 lines
9.5 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright 2020 Mellanox Technologies, Ltd
|
|
*/
|
|
|
|
#include "mlx5_flow_os.h"
|
|
#include "mlx5_win_ext.h"
|
|
|
|
#include <rte_thread.h>
|
|
|
|
/**
|
|
* Verify the @p attributes will be correctly understood by the NIC and store
|
|
* them in the @p flow if everything is correct.
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to dev struct.
|
|
* @param[in] attributes
|
|
* Pointer to flow attributes
|
|
* @param[in] external
|
|
* This flow rule is created by request external to PMD.
|
|
* @param[out] error
|
|
* Pointer to error structure.
|
|
*
|
|
* @return
|
|
* - 0 on success and non root table (not a valid option for Windows yet).
|
|
* - 1 on success and root table.
|
|
* - a negative errno value otherwise and rte_errno is set.
|
|
*/
|
|
int
|
|
mlx5_flow_os_validate_flow_attributes(struct rte_eth_dev *dev,
|
|
const struct rte_flow_attr *attributes,
|
|
bool external,
|
|
struct rte_flow_error *error)
|
|
{
|
|
int ret = 1;
|
|
|
|
RTE_SET_USED(dev);
|
|
RTE_SET_USED(external);
|
|
if (attributes->group)
|
|
return rte_flow_error_set(error, ENOTSUP,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
|
|
NULL,
|
|
"groups are not supported");
|
|
if (attributes->priority)
|
|
return rte_flow_error_set(error, ENOTSUP,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
|
|
NULL,
|
|
"priorities are not supported");
|
|
if (attributes->transfer)
|
|
return rte_flow_error_set(error, ENOTSUP,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,
|
|
NULL,
|
|
"transfer not supported");
|
|
if (!(attributes->ingress))
|
|
return rte_flow_error_set(error, ENOTSUP,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
|
|
NULL, "must specify ingress only");
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Create flow matcher in a flow table.
|
|
*
|
|
* @param[in] ctx
|
|
* Pointer to relevant device context.
|
|
* @param[in] attr
|
|
* Pointer to relevant attributes.
|
|
* @param[in] table
|
|
* Pointer to table object.
|
|
* @param[out] matcher
|
|
* Pointer to a valid flow matcher object on success, NULL otherwise.
|
|
*
|
|
* @return
|
|
* 0 on success, or errno on failure.
|
|
*/
|
|
int
|
|
mlx5_flow_os_create_flow_matcher(void *ctx,
|
|
void *attr,
|
|
void *table,
|
|
void **matcher)
|
|
{
|
|
struct mlx5dv_flow_matcher_attr *mattr;
|
|
|
|
RTE_SET_USED(table);
|
|
*matcher = NULL;
|
|
mattr = attr;
|
|
if (mattr->type != IBV_FLOW_ATTR_NORMAL) {
|
|
rte_errno = ENOTSUP;
|
|
return -rte_errno;
|
|
}
|
|
struct mlx5_matcher *mlx5_matcher =
|
|
mlx5_malloc(MLX5_MEM_ZERO,
|
|
sizeof(struct mlx5_matcher) +
|
|
MLX5_ST_SZ_BYTES(fte_match_param),
|
|
0, SOCKET_ID_ANY);
|
|
if (!mlx5_matcher) {
|
|
rte_errno = ENOMEM;
|
|
return -rte_errno;
|
|
}
|
|
mlx5_matcher->ctx = ctx;
|
|
memcpy(&mlx5_matcher->attr, attr, sizeof(mlx5_matcher->attr));
|
|
memcpy(&mlx5_matcher->match_buf,
|
|
mattr->match_mask->match_buf,
|
|
MLX5_ST_SZ_BYTES(fte_match_param));
|
|
*matcher = mlx5_matcher;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Destroy flow matcher.
|
|
*
|
|
* @param[in] matcher
|
|
* Pointer to matcher object to destroy.
|
|
*
|
|
* @return
|
|
* 0 on success, or the value of errno on failure.
|
|
*/
|
|
int
|
|
mlx5_flow_os_destroy_flow_matcher(void *matcher)
|
|
{
|
|
mlx5_free(matcher);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Create flow action: dest_devx_tir
|
|
*
|
|
* @param[in] tir
|
|
* Pointer to DevX tir object
|
|
* @param[out] action
|
|
* Pointer to a valid action on success, NULL otherwise.
|
|
*
|
|
* @return
|
|
* 0 on success, or errno on failure.
|
|
*/
|
|
int
|
|
mlx5_flow_os_create_flow_action_dest_devx_tir(struct mlx5_devx_obj *tir,
|
|
void **action)
|
|
{
|
|
struct mlx5_action *mlx5_action =
|
|
mlx5_malloc(MLX5_MEM_ZERO,
|
|
sizeof(struct mlx5_action),
|
|
0, SOCKET_ID_ANY);
|
|
|
|
if (!mlx5_action) {
|
|
rte_errno = ENOMEM;
|
|
return -rte_errno;
|
|
}
|
|
mlx5_action->type = MLX5_FLOW_CONTEXT_DEST_TYPE_TIR;
|
|
mlx5_action->dest_tir.id = tir->id;
|
|
*action = mlx5_action;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Destroy flow action.
|
|
*
|
|
* @param[in] action
|
|
* Pointer to action object to destroy.
|
|
*
|
|
* @return
|
|
* 0 on success, or the value of errno on failure.
|
|
*/
|
|
int
|
|
mlx5_flow_os_destroy_flow_action(void *action)
|
|
{
|
|
mlx5_free(action);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Create flow rule.
|
|
*
|
|
* @param[in] matcher
|
|
* Pointer to match mask structure.
|
|
* @param[in] match_value
|
|
* Pointer to match value structure.
|
|
* @param[in] num_actions
|
|
* Number of actions in flow rule.
|
|
* @param[in] actions
|
|
* Pointer to array of flow rule actions.
|
|
* @param[out] flow
|
|
* Pointer to a valid flow rule object on success, NULL otherwise.
|
|
*
|
|
* @return
|
|
* 0 on success, or errno on failure.
|
|
*/
|
|
int
|
|
mlx5_flow_os_create_flow(void *matcher, void *match_value,
|
|
size_t num_actions,
|
|
void *actions[], void **flow)
|
|
{
|
|
struct mlx5_action *action;
|
|
size_t i;
|
|
struct mlx5_matcher *mlx5_matcher = matcher;
|
|
struct mlx5_flow_dv_match_params *mlx5_match_value = match_value;
|
|
uint32_t in[MLX5_ST_SZ_DW(devx_fs_rule_add_in)] = {0};
|
|
void *matcher_c = MLX5_ADDR_OF(devx_fs_rule_add_in, in,
|
|
match_criteria);
|
|
void *matcher_v = MLX5_ADDR_OF(devx_fs_rule_add_in, in,
|
|
match_value);
|
|
|
|
MLX5_ASSERT(mlx5_matcher->ctx);
|
|
memcpy(matcher_c, mlx5_matcher->match_buf,
|
|
mlx5_match_value->size);
|
|
/* Use mlx5_match_value->size for match criteria */
|
|
memcpy(matcher_v, mlx5_match_value->buf,
|
|
mlx5_match_value->size);
|
|
for (i = 0; i < num_actions; i++) {
|
|
action = actions[i];
|
|
switch (action->type) {
|
|
case MLX5_FLOW_CONTEXT_DEST_TYPE_TIR:
|
|
MLX5_SET(devx_fs_rule_add_in, in,
|
|
dest.destination_type,
|
|
MLX5_FLOW_CONTEXT_DEST_TYPE_TIR);
|
|
MLX5_SET(devx_fs_rule_add_in, in,
|
|
dest.destination_id,
|
|
action->dest_tir.id);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
MLX5_SET(devx_fs_rule_add_in, in, match_criteria_enable,
|
|
MLX5_MATCH_OUTER_HEADERS);
|
|
}
|
|
*flow = mlx5_glue->devx_fs_rule_add(mlx5_matcher->ctx, in, sizeof(in));
|
|
return (*flow) ? 0 : -1;
|
|
}
|
|
|
|
/**
|
|
* Destroy flow rule.
|
|
*
|
|
* @param[in] drv_flow_ptr
|
|
* Pointer to flow rule object.
|
|
*
|
|
* @return
|
|
* 0 on success, errno on failure.
|
|
*/
|
|
int
|
|
mlx5_flow_os_destroy_flow(void *drv_flow_ptr)
|
|
{
|
|
return mlx5_glue->devx_fs_rule_del(drv_flow_ptr);
|
|
}
|
|
|
|
struct mlx5_workspace_thread {
|
|
HANDLE thread_handle;
|
|
struct mlx5_flow_workspace *mlx5_ws;
|
|
struct mlx5_workspace_thread *next;
|
|
};
|
|
|
|
/**
|
|
* Static pointer array for multi thread support of mlx5_flow_workspace.
|
|
*/
|
|
static struct mlx5_workspace_thread *curr;
|
|
static struct mlx5_workspace_thread *first;
|
|
rte_thread_key ws_tls_index;
|
|
static pthread_mutex_t lock_thread_list;
|
|
|
|
static bool
|
|
mlx5_is_thread_alive(HANDLE thread_handle)
|
|
{
|
|
DWORD result = WaitForSingleObject(thread_handle, 0);
|
|
|
|
if (result == WAIT_OBJECT_0)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static int
|
|
mlx5_get_current_thread(HANDLE *p_handle)
|
|
{
|
|
BOOL ret = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
|
|
GetCurrentProcess(), p_handle, 0, 0, DUPLICATE_SAME_ACCESS);
|
|
|
|
if (!ret) {
|
|
RTE_LOG_WIN32_ERR("DuplicateHandle()");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mlx5_clear_thread_list(void)
|
|
{
|
|
struct mlx5_workspace_thread *temp = first;
|
|
struct mlx5_workspace_thread *next, *prev = NULL;
|
|
HANDLE curr_thread;
|
|
|
|
if (!temp)
|
|
return;
|
|
if (mlx5_get_current_thread(&curr_thread)) {
|
|
DRV_LOG(ERR, "Failed to get current thread "
|
|
"handle.");
|
|
return;
|
|
}
|
|
while (temp) {
|
|
next = temp->next;
|
|
if (temp->thread_handle != curr_thread &&
|
|
!mlx5_is_thread_alive(temp->thread_handle)) {
|
|
if (temp == first) {
|
|
if (curr == temp)
|
|
curr = temp->next;
|
|
first = temp->next;
|
|
} else if (temp == curr) {
|
|
curr = prev;
|
|
}
|
|
flow_release_workspace(temp->mlx5_ws);
|
|
CloseHandle(temp->thread_handle);
|
|
free(temp);
|
|
if (prev)
|
|
prev->next = next;
|
|
temp = next;
|
|
continue;
|
|
}
|
|
prev = temp;
|
|
temp = temp->next;
|
|
}
|
|
CloseHandle(curr_thread);
|
|
}
|
|
|
|
/**
|
|
* Release workspaces before exit.
|
|
*/
|
|
void
|
|
mlx5_flow_os_release_workspace(void)
|
|
{
|
|
mlx5_clear_thread_list();
|
|
if (first) {
|
|
MLX5_ASSERT(!first->next);
|
|
flow_release_workspace(first->mlx5_ws);
|
|
free(first);
|
|
}
|
|
rte_thread_key_delete(ws_tls_index);
|
|
pthread_mutex_destroy(&lock_thread_list);
|
|
}
|
|
|
|
static int
|
|
mlx5_add_workspace_to_list(struct mlx5_flow_workspace *data)
|
|
{
|
|
HANDLE curr_thread;
|
|
struct mlx5_workspace_thread *temp = calloc(1, sizeof(*temp));
|
|
|
|
if (!temp) {
|
|
DRV_LOG(ERR, "Failed to allocate thread workspace "
|
|
"memory.");
|
|
return -1;
|
|
}
|
|
if (mlx5_get_current_thread(&curr_thread)) {
|
|
DRV_LOG(ERR, "Failed to get current thread "
|
|
"handle.");
|
|
free(temp);
|
|
return -1;
|
|
}
|
|
temp->mlx5_ws = data;
|
|
temp->thread_handle = curr_thread;
|
|
pthread_mutex_lock(&lock_thread_list);
|
|
mlx5_clear_thread_list();
|
|
if (!first) {
|
|
first = temp;
|
|
curr = temp;
|
|
} else {
|
|
curr->next = temp;
|
|
curr = curr->next;
|
|
}
|
|
pthread_mutex_unlock(&lock_thread_list);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mlx5_flow_os_init_workspace_once(void)
|
|
{
|
|
int err = rte_thread_key_create(&ws_tls_index, NULL);
|
|
|
|
if (err) {
|
|
DRV_LOG(ERR, "Can't create flow workspace data thread key.");
|
|
return -rte_errno;
|
|
}
|
|
pthread_mutex_init(&lock_thread_list, NULL);
|
|
return 0;
|
|
}
|
|
|
|
void *
|
|
mlx5_flow_os_get_specific_workspace(void)
|
|
{
|
|
return rte_thread_value_get(ws_tls_index);
|
|
}
|
|
|
|
int
|
|
mlx5_flow_os_set_specific_workspace(struct mlx5_flow_workspace *data)
|
|
{
|
|
int err = 0;
|
|
int old_err = rte_errno;
|
|
|
|
rte_errno = 0;
|
|
if (!rte_thread_value_get(ws_tls_index)) {
|
|
if (rte_errno) {
|
|
DRV_LOG(ERR, "Failed checking specific workspace.");
|
|
rte_errno = old_err;
|
|
return -1;
|
|
}
|
|
/*
|
|
* set_specific_workspace when current value is NULL
|
|
* can happen only once per thread, mark this thread in
|
|
* linked list to be able to release resources later on.
|
|
*/
|
|
err = mlx5_add_workspace_to_list(data);
|
|
if (err) {
|
|
DRV_LOG(ERR, "Failed adding workspace to list.");
|
|
rte_errno = old_err;
|
|
return -1;
|
|
}
|
|
}
|
|
if (rte_thread_value_set(ws_tls_index, data)) {
|
|
DRV_LOG(ERR, "Failed setting specific workspace.");
|
|
err = -1;
|
|
}
|
|
rte_errno = old_err;
|
|
return err;
|
|
}
|