Skip to content

Commit

Permalink
i40e: Implement DDP support in i40e driver
Browse files Browse the repository at this point in the history
This patch introduces DDP (Dynamic Device Personalization) which allows
loading profiles that change the way internal parser interprets processed
frames. To load DDP profiles it utilizes ethtool flash feature. The files
with recipes must be located in /var/lib/firmware directory. Afterwards
the recipe can be loaded by invoking:

    ethtool -f <if_name> <file_name> 100
    ethtool -f <if_name> - 100

See further details of this feature in the i40e documentation, or
visit
https://www.intel.com/content/www/us/en/architecture-and-technology/ethernet/dynamic-device-personalization-brief.html

The driver shall verify DDP profile can be loaded in accordance with
the rules:
* Package with Group ID 0 are exclusive and can only be loaded the first.
* Packages with Group ID 0x01-0xFE can only be loaded simultaneously
   with the packages from the same group.
* Packages with Group ID 0xFF are compatible with all other packages.

Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
  • Loading branch information
aloktion authored and Jeff Kirsher committed Apr 16, 2019
1 parent 3e957b3 commit cdc594e
Show file tree
Hide file tree
Showing 8 changed files with 769 additions and 22 deletions.
1 change: 1 addition & 0 deletions drivers/net/ethernet/intel/i40e/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ i40e-objs := i40e_main.o \
i40e_diag.o \
i40e_txrx.o \
i40e_ptp.o \
i40e_ddp.o \
i40e_client.o \
i40e_virtchnl_pf.o \
i40e_xsk.o
Expand Down
25 changes: 25 additions & 0 deletions drivers/net/ethernet/intel/i40e/i40e.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,29 @@ struct i40e_udp_port_config {
u8 filter_index;
};

#define I40_DDP_FLASH_REGION 100
#define I40E_PROFILE_INFO_SIZE 48
#define I40E_MAX_PROFILE_NUM 16
#define I40E_PROFILE_LIST_SIZE \
(I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4)
#define I40E_DDP_PROFILE_PATH "intel/i40e/ddp/"
#define I40E_DDP_PROFILE_NAME_MAX 64

int i40e_ddp_load(struct net_device *netdev, const u8 *data, size_t size,
bool is_add);
int i40e_ddp_flash(struct net_device *netdev, struct ethtool_flash *flash);

struct i40e_ddp_profile_list {
u32 p_count;
struct i40e_profile_info p_info[0];
};

struct i40e_ddp_old_profile_list {
struct list_head list;
size_t old_ddp_size;
u8 old_ddp_buf[0];
};

/* macros related to FLX_PIT */
#define I40E_FLEX_SET_FSIZE(fsize) (((fsize) << \
I40E_PRTQF_FLX_PIT_FSIZE_SHIFT) & \
Expand Down Expand Up @@ -610,6 +633,8 @@ struct i40e_pf {
u16 override_q_count;
u16 last_sw_conf_flags;
u16 last_sw_conf_valid_flags;
/* List to keep previous DDP profiles to be rolled back in the future */
struct list_head ddp_old_prof;
};

/**
Expand Down
253 changes: 231 additions & 22 deletions drivers/net/ethernet/intel/i40e/i40e_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -5448,6 +5448,163 @@ i40e_find_segment_in_package(u32 segment_type,
return NULL;
}

/* Get section table in profile */
#define I40E_SECTION_TABLE(profile, sec_tbl) \
do { \
struct i40e_profile_segment *p = (profile); \
u32 count; \
u32 *nvm; \
count = p->device_table_count; \
nvm = (u32 *)&p->device_table[count]; \
sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; \
} while (0)

/* Get section header in profile */
#define I40E_SECTION_HEADER(profile, offset) \
(struct i40e_profile_section_header *)((u8 *)(profile) + (offset))

/**
* i40e_find_section_in_profile
* @section_type: the section type to search for (i.e., SECTION_TYPE_NOTE)
* @profile: pointer to the i40e segment header to be searched
*
* This function searches i40e segment for a particular section type. On
* success it returns a pointer to the section header, otherwise it will
* return NULL.
**/
struct i40e_profile_section_header *
i40e_find_section_in_profile(u32 section_type,
struct i40e_profile_segment *profile)
{
struct i40e_profile_section_header *sec;
struct i40e_section_table *sec_tbl;
u32 sec_off;
u32 i;

if (profile->header.type != SEGMENT_TYPE_I40E)
return NULL;

I40E_SECTION_TABLE(profile, sec_tbl);

for (i = 0; i < sec_tbl->section_count; i++) {
sec_off = sec_tbl->section_offset[i];
sec = I40E_SECTION_HEADER(profile, sec_off);
if (sec->section.type == section_type)
return sec;
}

return NULL;
}

/**
* i40e_ddp_exec_aq_section - Execute generic AQ for DDP
* @hw: pointer to the hw struct
* @aq: command buffer containing all data to execute AQ
**/
static enum
i40e_status_code i40e_ddp_exec_aq_section(struct i40e_hw *hw,
struct i40e_profile_aq_section *aq)
{
i40e_status status;
struct i40e_aq_desc desc;
u8 *msg = NULL;
u16 msglen;

i40e_fill_default_direct_cmd_desc(&desc, aq->opcode);
desc.flags |= cpu_to_le16(aq->flags);
memcpy(desc.params.raw, aq->param, sizeof(desc.params.raw));

msglen = aq->datalen;
if (msglen) {
desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF |
I40E_AQ_FLAG_RD));
if (msglen > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
desc.datalen = cpu_to_le16(msglen);
msg = &aq->data[0];
}

status = i40e_asq_send_command(hw, &desc, msg, msglen, NULL);

if (status) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"unable to exec DDP AQ opcode %u, error %d\n",
aq->opcode, status);
return status;
}

/* copy returned desc to aq_buf */
memcpy(aq->param, desc.params.raw, sizeof(desc.params.raw));

return 0;
}

/**
* i40e_validate_profile
* @hw: pointer to the hardware structure
* @profile: pointer to the profile segment of the package to be validated
* @track_id: package tracking id
* @rollback: flag if the profile is for rollback.
*
* Validates supported devices and profile's sections.
*/
static enum i40e_status_code
i40e_validate_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
u32 track_id, bool rollback)
{
struct i40e_profile_section_header *sec = NULL;
i40e_status status = 0;
struct i40e_section_table *sec_tbl;
u32 vendor_dev_id;
u32 dev_cnt;
u32 sec_off;
u32 i;

if (track_id == I40E_DDP_TRACKID_INVALID) {
i40e_debug(hw, I40E_DEBUG_PACKAGE, "Invalid track_id\n");
return I40E_NOT_SUPPORTED;
}

dev_cnt = profile->device_table_count;
for (i = 0; i < dev_cnt; i++) {
vendor_dev_id = profile->device_table[i].vendor_dev_id;
if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL &&
hw->device_id == (vendor_dev_id & 0xFFFF))
break;
}
if (dev_cnt && i == dev_cnt) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Device doesn't support DDP\n");
return I40E_ERR_DEVICE_NOT_SUPPORTED;
}

I40E_SECTION_TABLE(profile, sec_tbl);

/* Validate sections types */
for (i = 0; i < sec_tbl->section_count; i++) {
sec_off = sec_tbl->section_offset[i];
sec = I40E_SECTION_HEADER(profile, sec_off);
if (rollback) {
if (sec->section.type == SECTION_TYPE_MMIO ||
sec->section.type == SECTION_TYPE_AQ ||
sec->section.type == SECTION_TYPE_RB_AQ) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Not a roll-back package\n");
return I40E_NOT_SUPPORTED;
}
} else {
if (sec->section.type == SECTION_TYPE_RB_AQ ||
sec->section.type == SECTION_TYPE_RB_MMIO) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Not an original package\n");
return I40E_NOT_SUPPORTED;
}
}
}

return status;
}

/**
* i40e_write_profile
* @hw: pointer to the hardware structure
Expand All @@ -5463,47 +5620,99 @@ i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
i40e_status status = 0;
struct i40e_section_table *sec_tbl;
struct i40e_profile_section_header *sec = NULL;
u32 dev_cnt;
u32 vendor_dev_id;
u32 *nvm;
struct i40e_profile_aq_section *ddp_aq;
u32 section_size = 0;
u32 offset = 0, info = 0;
u32 sec_off;
u32 i;

dev_cnt = profile->device_table_count;
status = i40e_validate_profile(hw, profile, track_id, false);
if (status)
return status;

for (i = 0; i < dev_cnt; i++) {
vendor_dev_id = profile->device_table[i].vendor_dev_id;
if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL)
if (hw->device_id == (vendor_dev_id & 0xFFFF))
I40E_SECTION_TABLE(profile, sec_tbl);

for (i = 0; i < sec_tbl->section_count; i++) {
sec_off = sec_tbl->section_offset[i];
sec = I40E_SECTION_HEADER(profile, sec_off);
/* Process generic admin command */
if (sec->section.type == SECTION_TYPE_AQ) {
ddp_aq = (struct i40e_profile_aq_section *)&sec[1];
status = i40e_ddp_exec_aq_section(hw, ddp_aq);
if (status) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Failed to execute aq: section %d, opcode %u\n",
i, ddp_aq->opcode);
break;
}
sec->section.type = SECTION_TYPE_RB_AQ;
}

/* Skip any non-mmio sections */
if (sec->section.type != SECTION_TYPE_MMIO)
continue;

section_size = sec->section.size +
sizeof(struct i40e_profile_section_header);

/* Write MMIO section */
status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
track_id, &offset, &info, NULL);
if (status) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Failed to write profile: section %d, offset %d, info %d\n",
i, offset, info);
break;
}
}
if (i == dev_cnt) {
i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support DDP");
return I40E_ERR_DEVICE_NOT_SUPPORTED;
}
return status;
}

/**
* i40e_rollback_profile
* @hw: pointer to the hardware structure
* @profile: pointer to the profile segment of the package to be removed
* @track_id: package tracking id
*
* Rolls back previously loaded package.
*/
enum i40e_status_code
i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
u32 track_id)
{
struct i40e_profile_section_header *sec = NULL;
i40e_status status = 0;
struct i40e_section_table *sec_tbl;
u32 offset = 0, info = 0;
u32 section_size = 0;
u32 sec_off;
int i;

nvm = (u32 *)&profile->device_table[dev_cnt];
sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1];
status = i40e_validate_profile(hw, profile, track_id, true);
if (status)
return status;

for (i = 0; i < sec_tbl->section_count; i++) {
sec = (struct i40e_profile_section_header *)((u8 *)profile +
sec_tbl->section_offset[i]);
I40E_SECTION_TABLE(profile, sec_tbl);

/* Skip 'AQ', 'note' and 'name' sections */
if (sec->section.type != SECTION_TYPE_MMIO)
/* For rollback write sections in reverse */
for (i = sec_tbl->section_count - 1; i >= 0; i--) {
sec_off = sec_tbl->section_offset[i];
sec = I40E_SECTION_HEADER(profile, sec_off);

/* Skip any non-rollback sections */
if (sec->section.type != SECTION_TYPE_RB_MMIO)
continue;

section_size = sec->section.size +
sizeof(struct i40e_profile_section_header);

/* Write profile */
/* Write roll-back MMIO section */
status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
track_id, &offset, &info, NULL);
if (status) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Failed to write profile: offset %d, info %d",
offset, info);
"Failed to write profile: section %d, offset %d, info %d\n",
i, offset, info);
break;
}
}
Expand Down
Loading

0 comments on commit cdc594e

Please sign in to comment.