251 lines
8.1 KiB
C
251 lines
8.1 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/* Copyright (c) 2019, Intel Corporation. */
|
||
|
|
||
|
#include "ice_common.h"
|
||
|
#include "ice_flow.h"
|
||
|
|
||
|
/* Describe properties of a protocol header field */
|
||
|
struct ice_flow_field_info {
|
||
|
enum ice_flow_seg_hdr hdr;
|
||
|
s16 off; /* Offset from start of a protocol header, in bits */
|
||
|
u16 size; /* Size of fields in bits */
|
||
|
};
|
||
|
|
||
|
#define ICE_FLOW_FLD_INFO(_hdr, _offset_bytes, _size_bytes) { \
|
||
|
.hdr = _hdr, \
|
||
|
.off = (_offset_bytes) * BITS_PER_BYTE, \
|
||
|
.size = (_size_bytes) * BITS_PER_BYTE, \
|
||
|
}
|
||
|
|
||
|
/* Table containing properties of supported protocol header fields */
|
||
|
static const
|
||
|
struct ice_flow_field_info ice_flds_info[ICE_FLOW_FIELD_IDX_MAX] = {
|
||
|
/* IPv4 / IPv6 */
|
||
|
/* ICE_FLOW_FIELD_IDX_IPV4_SA */
|
||
|
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV4, 12, sizeof(struct in_addr)),
|
||
|
/* ICE_FLOW_FIELD_IDX_IPV4_DA */
|
||
|
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV4, 16, sizeof(struct in_addr)),
|
||
|
/* ICE_FLOW_FIELD_IDX_IPV6_SA */
|
||
|
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 8, sizeof(struct in6_addr)),
|
||
|
/* ICE_FLOW_FIELD_IDX_IPV6_DA */
|
||
|
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 24, sizeof(struct in6_addr)),
|
||
|
/* Transport */
|
||
|
/* ICE_FLOW_FIELD_IDX_TCP_SRC_PORT */
|
||
|
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 0, sizeof(__be16)),
|
||
|
/* ICE_FLOW_FIELD_IDX_TCP_DST_PORT */
|
||
|
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 2, sizeof(__be16)),
|
||
|
/* ICE_FLOW_FIELD_IDX_UDP_SRC_PORT */
|
||
|
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_UDP, 0, sizeof(__be16)),
|
||
|
/* ICE_FLOW_FIELD_IDX_UDP_DST_PORT */
|
||
|
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_UDP, 2, sizeof(__be16)),
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* ice_flow_set_fld_ext - specifies locations of field from entry's input buffer
|
||
|
* @seg: packet segment the field being set belongs to
|
||
|
* @fld: field to be set
|
||
|
* @type: type of the field
|
||
|
* @val_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of the value to match from
|
||
|
* entry's input buffer
|
||
|
* @mask_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of mask value from entry's
|
||
|
* input buffer
|
||
|
* @last_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of last/upper value from
|
||
|
* entry's input buffer
|
||
|
*
|
||
|
* This helper function stores information of a field being matched, including
|
||
|
* the type of the field and the locations of the value to match, the mask, and
|
||
|
* and the upper-bound value in the start of the input buffer for a flow entry.
|
||
|
* This function should only be used for fixed-size data structures.
|
||
|
*
|
||
|
* This function also opportunistically determines the protocol headers to be
|
||
|
* present based on the fields being set. Some fields cannot be used alone to
|
||
|
* determine the protocol headers present. Sometimes, fields for particular
|
||
|
* protocol headers are not matched. In those cases, the protocol headers
|
||
|
* must be explicitly set.
|
||
|
*/
|
||
|
static void
|
||
|
ice_flow_set_fld_ext(struct ice_flow_seg_info *seg, enum ice_flow_field fld,
|
||
|
enum ice_flow_fld_match_type type, u16 val_loc,
|
||
|
u16 mask_loc, u16 last_loc)
|
||
|
{
|
||
|
u64 bit = BIT_ULL(fld);
|
||
|
|
||
|
seg->match |= bit;
|
||
|
if (type == ICE_FLOW_FLD_TYPE_RANGE)
|
||
|
seg->range |= bit;
|
||
|
|
||
|
seg->fields[fld].type = type;
|
||
|
seg->fields[fld].src.val = val_loc;
|
||
|
seg->fields[fld].src.mask = mask_loc;
|
||
|
seg->fields[fld].src.last = last_loc;
|
||
|
|
||
|
ICE_FLOW_SET_HDRS(seg, ice_flds_info[fld].hdr);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ice_flow_set_fld - specifies locations of field from entry's input buffer
|
||
|
* @seg: packet segment the field being set belongs to
|
||
|
* @fld: field to be set
|
||
|
* @val_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of the value to match from
|
||
|
* entry's input buffer
|
||
|
* @mask_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of mask value from entry's
|
||
|
* input buffer
|
||
|
* @last_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of last/upper value from
|
||
|
* entry's input buffer
|
||
|
* @range: indicate if field being matched is to be in a range
|
||
|
*
|
||
|
* This function specifies the locations, in the form of byte offsets from the
|
||
|
* start of the input buffer for a flow entry, from where the value to match,
|
||
|
* the mask value, and upper value can be extracted. These locations are then
|
||
|
* stored in the flow profile. When adding a flow entry associated with the
|
||
|
* flow profile, these locations will be used to quickly extract the values and
|
||
|
* create the content of a match entry. This function should only be used for
|
||
|
* fixed-size data structures.
|
||
|
*/
|
||
|
static void
|
||
|
ice_flow_set_fld(struct ice_flow_seg_info *seg, enum ice_flow_field fld,
|
||
|
u16 val_loc, u16 mask_loc, u16 last_loc, bool range)
|
||
|
{
|
||
|
enum ice_flow_fld_match_type t = range ?
|
||
|
ICE_FLOW_FLD_TYPE_RANGE : ICE_FLOW_FLD_TYPE_REG;
|
||
|
|
||
|
ice_flow_set_fld_ext(seg, fld, t, val_loc, mask_loc, last_loc);
|
||
|
}
|
||
|
|
||
|
#define ICE_FLOW_RSS_SEG_HDR_L3_MASKS \
|
||
|
(ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV6)
|
||
|
|
||
|
#define ICE_FLOW_RSS_SEG_HDR_L4_MASKS \
|
||
|
(ICE_FLOW_SEG_HDR_TCP | ICE_FLOW_SEG_HDR_UDP | ICE_FLOW_SEG_HDR_SCTP)
|
||
|
|
||
|
#define ICE_FLOW_RSS_SEG_HDR_VAL_MASKS \
|
||
|
(ICE_FLOW_RSS_SEG_HDR_L3_MASKS | \
|
||
|
ICE_FLOW_RSS_SEG_HDR_L4_MASKS)
|
||
|
|
||
|
/**
|
||
|
* ice_flow_set_rss_seg_info - setup packet segments for RSS
|
||
|
* @segs: pointer to the flow field segment(s)
|
||
|
* @hash_fields: fields to be hashed on for the segment(s)
|
||
|
* @flow_hdr: protocol header fields within a packet segment
|
||
|
*
|
||
|
* Helper function to extract fields from hash bitmap and use flow
|
||
|
* header value to set flow field segment for further use in flow
|
||
|
* profile entry or removal.
|
||
|
*/
|
||
|
static enum ice_status
|
||
|
ice_flow_set_rss_seg_info(struct ice_flow_seg_info *segs, u64 hash_fields,
|
||
|
u32 flow_hdr)
|
||
|
{
|
||
|
u64 val;
|
||
|
u8 i;
|
||
|
|
||
|
for_each_set_bit(i, (unsigned long *)&hash_fields,
|
||
|
ICE_FLOW_FIELD_IDX_MAX)
|
||
|
ice_flow_set_fld(segs, (enum ice_flow_field)i,
|
||
|
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
|
||
|
ICE_FLOW_FLD_OFF_INVAL, false);
|
||
|
|
||
|
ICE_FLOW_SET_HDRS(segs, flow_hdr);
|
||
|
|
||
|
if (segs->hdrs & ~ICE_FLOW_RSS_SEG_HDR_VAL_MASKS)
|
||
|
return ICE_ERR_PARAM;
|
||
|
|
||
|
val = (u64)(segs->hdrs & ICE_FLOW_RSS_SEG_HDR_L3_MASKS);
|
||
|
if (val && !is_power_of_2(val))
|
||
|
return ICE_ERR_CFG;
|
||
|
|
||
|
val = (u64)(segs->hdrs & ICE_FLOW_RSS_SEG_HDR_L4_MASKS);
|
||
|
if (val && !is_power_of_2(val))
|
||
|
return ICE_ERR_CFG;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#define ICE_RSS_OUTER_HEADERS 1
|
||
|
|
||
|
/**
|
||
|
* ice_add_rss_cfg_sync - add an RSS configuration
|
||
|
* @hashed_flds: hash bit fields (ICE_FLOW_HASH_*) to configure
|
||
|
* @addl_hdrs: protocol header fields
|
||
|
* @segs_cnt: packet segment count
|
||
|
*
|
||
|
* Assumption: lock has already been acquired for RSS list
|
||
|
*/
|
||
|
static enum ice_status
|
||
|
ice_add_rss_cfg_sync(u64 hashed_flds, u32 addl_hdrs, u8 segs_cnt)
|
||
|
{
|
||
|
struct ice_flow_seg_info *segs;
|
||
|
enum ice_status status;
|
||
|
|
||
|
if (!segs_cnt || segs_cnt > ICE_FLOW_SEG_MAX)
|
||
|
return ICE_ERR_PARAM;
|
||
|
|
||
|
segs = kcalloc(segs_cnt, sizeof(*segs), GFP_KERNEL);
|
||
|
if (!segs)
|
||
|
return ICE_ERR_NO_MEMORY;
|
||
|
|
||
|
/* Construct the packet segment info from the hashed fields */
|
||
|
status = ice_flow_set_rss_seg_info(&segs[segs_cnt - 1], hashed_flds,
|
||
|
addl_hdrs);
|
||
|
|
||
|
kfree(segs);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ice_add_rss_cfg - add an RSS configuration with specified hashed fields
|
||
|
* @hw: pointer to the hardware structure
|
||
|
* @vsi_handle: software VSI handle
|
||
|
* @hashed_flds: hash bit fields (ICE_FLOW_HASH_*) to configure
|
||
|
* @addl_hdrs: protocol header fields
|
||
|
*
|
||
|
* This function will generate a flow profile based on fields associated with
|
||
|
* the input fields to hash on, the flow type and use the VSI number to add
|
||
|
* a flow entry to the profile.
|
||
|
*/
|
||
|
enum ice_status
|
||
|
ice_add_rss_cfg(struct ice_hw *hw, u16 vsi_handle, u64 hashed_flds,
|
||
|
u32 addl_hdrs)
|
||
|
{
|
||
|
enum ice_status status;
|
||
|
|
||
|
if (hashed_flds == ICE_HASH_INVALID ||
|
||
|
!ice_is_vsi_valid(hw, vsi_handle))
|
||
|
return ICE_ERR_PARAM;
|
||
|
|
||
|
mutex_lock(&hw->rss_locks);
|
||
|
status = ice_add_rss_cfg_sync(hashed_flds, addl_hdrs,
|
||
|
ICE_RSS_OUTER_HEADERS);
|
||
|
mutex_unlock(&hw->rss_locks);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ice_replay_rss_cfg - replay RSS configurations associated with VSI
|
||
|
* @hw: pointer to the hardware structure
|
||
|
* @vsi_handle: software VSI handle
|
||
|
*/
|
||
|
enum ice_status ice_replay_rss_cfg(struct ice_hw *hw, u16 vsi_handle)
|
||
|
{
|
||
|
enum ice_status status = 0;
|
||
|
struct ice_rss_cfg *r;
|
||
|
|
||
|
if (!ice_is_vsi_valid(hw, vsi_handle))
|
||
|
return ICE_ERR_PARAM;
|
||
|
|
||
|
mutex_lock(&hw->rss_locks);
|
||
|
list_for_each_entry(r, &hw->rss_list_head, l_entry) {
|
||
|
if (test_bit(vsi_handle, r->vsis)) {
|
||
|
status = ice_add_rss_cfg_sync(r->hashed_flds,
|
||
|
r->packet_hdr,
|
||
|
ICE_RSS_OUTER_HEADERS);
|
||
|
if (status)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
mutex_unlock(&hw->rss_locks);
|
||
|
|
||
|
return status;
|
||
|
}
|