forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ice: Enable writing hardware filtering tables
Enable the driver to write the filtering hardware tables to allow for changing of RSS rules. Upon loading of DDP package, a minimal configuration should be written to hardware. Introduce and initialize structures for storing configuration and make the top level calls to configure the RSS tables to initial values. A packet segment will be created but nothing is written to hardware yet. Signed-off-by: Tony Nguyen <[email protected]> Signed-off-by: Henry Tieman <[email protected]> Tested-by: Andrew Bowers <[email protected]> Signed-off-by: Jeff Kirsher <[email protected]>
- Loading branch information
Showing
7 changed files
with
491 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
// 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; | ||
} |
Oops, something went wrong.