Skip to content

Commit cdc594e

Browse files
aloktionJeff Kirsher
authored andcommitted
i40e: Implement DDP support in i40e driver
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 <[email protected]> Tested-by: Andrew Bowers <[email protected]> Signed-off-by: Jeff Kirsher <[email protected]>
1 parent 3e957b3 commit cdc594e

File tree

8 files changed

+769
-22
lines changed

8 files changed

+769
-22
lines changed

drivers/net/ethernet/intel/i40e/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ i40e-objs := i40e_main.o \
2121
i40e_diag.o \
2222
i40e_txrx.o \
2323
i40e_ptp.o \
24+
i40e_ddp.o \
2425
i40e_client.o \
2526
i40e_virtchnl_pf.o \
2627
i40e_xsk.o

drivers/net/ethernet/intel/i40e/i40e.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,29 @@ struct i40e_udp_port_config {
321321
u8 filter_index;
322322
};
323323

324+
#define I40_DDP_FLASH_REGION 100
325+
#define I40E_PROFILE_INFO_SIZE 48
326+
#define I40E_MAX_PROFILE_NUM 16
327+
#define I40E_PROFILE_LIST_SIZE \
328+
(I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4)
329+
#define I40E_DDP_PROFILE_PATH "intel/i40e/ddp/"
330+
#define I40E_DDP_PROFILE_NAME_MAX 64
331+
332+
int i40e_ddp_load(struct net_device *netdev, const u8 *data, size_t size,
333+
bool is_add);
334+
int i40e_ddp_flash(struct net_device *netdev, struct ethtool_flash *flash);
335+
336+
struct i40e_ddp_profile_list {
337+
u32 p_count;
338+
struct i40e_profile_info p_info[0];
339+
};
340+
341+
struct i40e_ddp_old_profile_list {
342+
struct list_head list;
343+
size_t old_ddp_size;
344+
u8 old_ddp_buf[0];
345+
};
346+
324347
/* macros related to FLX_PIT */
325348
#define I40E_FLEX_SET_FSIZE(fsize) (((fsize) << \
326349
I40E_PRTQF_FLX_PIT_FSIZE_SHIFT) & \
@@ -610,6 +633,8 @@ struct i40e_pf {
610633
u16 override_q_count;
611634
u16 last_sw_conf_flags;
612635
u16 last_sw_conf_valid_flags;
636+
/* List to keep previous DDP profiles to be rolled back in the future */
637+
struct list_head ddp_old_prof;
613638
};
614639

615640
/**

drivers/net/ethernet/intel/i40e/i40e_common.c

Lines changed: 231 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5448,6 +5448,163 @@ i40e_find_segment_in_package(u32 segment_type,
54485448
return NULL;
54495449
}
54505450

5451+
/* Get section table in profile */
5452+
#define I40E_SECTION_TABLE(profile, sec_tbl) \
5453+
do { \
5454+
struct i40e_profile_segment *p = (profile); \
5455+
u32 count; \
5456+
u32 *nvm; \
5457+
count = p->device_table_count; \
5458+
nvm = (u32 *)&p->device_table[count]; \
5459+
sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; \
5460+
} while (0)
5461+
5462+
/* Get section header in profile */
5463+
#define I40E_SECTION_HEADER(profile, offset) \
5464+
(struct i40e_profile_section_header *)((u8 *)(profile) + (offset))
5465+
5466+
/**
5467+
* i40e_find_section_in_profile
5468+
* @section_type: the section type to search for (i.e., SECTION_TYPE_NOTE)
5469+
* @profile: pointer to the i40e segment header to be searched
5470+
*
5471+
* This function searches i40e segment for a particular section type. On
5472+
* success it returns a pointer to the section header, otherwise it will
5473+
* return NULL.
5474+
**/
5475+
struct i40e_profile_section_header *
5476+
i40e_find_section_in_profile(u32 section_type,
5477+
struct i40e_profile_segment *profile)
5478+
{
5479+
struct i40e_profile_section_header *sec;
5480+
struct i40e_section_table *sec_tbl;
5481+
u32 sec_off;
5482+
u32 i;
5483+
5484+
if (profile->header.type != SEGMENT_TYPE_I40E)
5485+
return NULL;
5486+
5487+
I40E_SECTION_TABLE(profile, sec_tbl);
5488+
5489+
for (i = 0; i < sec_tbl->section_count; i++) {
5490+
sec_off = sec_tbl->section_offset[i];
5491+
sec = I40E_SECTION_HEADER(profile, sec_off);
5492+
if (sec->section.type == section_type)
5493+
return sec;
5494+
}
5495+
5496+
return NULL;
5497+
}
5498+
5499+
/**
5500+
* i40e_ddp_exec_aq_section - Execute generic AQ for DDP
5501+
* @hw: pointer to the hw struct
5502+
* @aq: command buffer containing all data to execute AQ
5503+
**/
5504+
static enum
5505+
i40e_status_code i40e_ddp_exec_aq_section(struct i40e_hw *hw,
5506+
struct i40e_profile_aq_section *aq)
5507+
{
5508+
i40e_status status;
5509+
struct i40e_aq_desc desc;
5510+
u8 *msg = NULL;
5511+
u16 msglen;
5512+
5513+
i40e_fill_default_direct_cmd_desc(&desc, aq->opcode);
5514+
desc.flags |= cpu_to_le16(aq->flags);
5515+
memcpy(desc.params.raw, aq->param, sizeof(desc.params.raw));
5516+
5517+
msglen = aq->datalen;
5518+
if (msglen) {
5519+
desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF |
5520+
I40E_AQ_FLAG_RD));
5521+
if (msglen > I40E_AQ_LARGE_BUF)
5522+
desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
5523+
desc.datalen = cpu_to_le16(msglen);
5524+
msg = &aq->data[0];
5525+
}
5526+
5527+
status = i40e_asq_send_command(hw, &desc, msg, msglen, NULL);
5528+
5529+
if (status) {
5530+
i40e_debug(hw, I40E_DEBUG_PACKAGE,
5531+
"unable to exec DDP AQ opcode %u, error %d\n",
5532+
aq->opcode, status);
5533+
return status;
5534+
}
5535+
5536+
/* copy returned desc to aq_buf */
5537+
memcpy(aq->param, desc.params.raw, sizeof(desc.params.raw));
5538+
5539+
return 0;
5540+
}
5541+
5542+
/**
5543+
* i40e_validate_profile
5544+
* @hw: pointer to the hardware structure
5545+
* @profile: pointer to the profile segment of the package to be validated
5546+
* @track_id: package tracking id
5547+
* @rollback: flag if the profile is for rollback.
5548+
*
5549+
* Validates supported devices and profile's sections.
5550+
*/
5551+
static enum i40e_status_code
5552+
i40e_validate_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
5553+
u32 track_id, bool rollback)
5554+
{
5555+
struct i40e_profile_section_header *sec = NULL;
5556+
i40e_status status = 0;
5557+
struct i40e_section_table *sec_tbl;
5558+
u32 vendor_dev_id;
5559+
u32 dev_cnt;
5560+
u32 sec_off;
5561+
u32 i;
5562+
5563+
if (track_id == I40E_DDP_TRACKID_INVALID) {
5564+
i40e_debug(hw, I40E_DEBUG_PACKAGE, "Invalid track_id\n");
5565+
return I40E_NOT_SUPPORTED;
5566+
}
5567+
5568+
dev_cnt = profile->device_table_count;
5569+
for (i = 0; i < dev_cnt; i++) {
5570+
vendor_dev_id = profile->device_table[i].vendor_dev_id;
5571+
if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL &&
5572+
hw->device_id == (vendor_dev_id & 0xFFFF))
5573+
break;
5574+
}
5575+
if (dev_cnt && i == dev_cnt) {
5576+
i40e_debug(hw, I40E_DEBUG_PACKAGE,
5577+
"Device doesn't support DDP\n");
5578+
return I40E_ERR_DEVICE_NOT_SUPPORTED;
5579+
}
5580+
5581+
I40E_SECTION_TABLE(profile, sec_tbl);
5582+
5583+
/* Validate sections types */
5584+
for (i = 0; i < sec_tbl->section_count; i++) {
5585+
sec_off = sec_tbl->section_offset[i];
5586+
sec = I40E_SECTION_HEADER(profile, sec_off);
5587+
if (rollback) {
5588+
if (sec->section.type == SECTION_TYPE_MMIO ||
5589+
sec->section.type == SECTION_TYPE_AQ ||
5590+
sec->section.type == SECTION_TYPE_RB_AQ) {
5591+
i40e_debug(hw, I40E_DEBUG_PACKAGE,
5592+
"Not a roll-back package\n");
5593+
return I40E_NOT_SUPPORTED;
5594+
}
5595+
} else {
5596+
if (sec->section.type == SECTION_TYPE_RB_AQ ||
5597+
sec->section.type == SECTION_TYPE_RB_MMIO) {
5598+
i40e_debug(hw, I40E_DEBUG_PACKAGE,
5599+
"Not an original package\n");
5600+
return I40E_NOT_SUPPORTED;
5601+
}
5602+
}
5603+
}
5604+
5605+
return status;
5606+
}
5607+
54515608
/**
54525609
* i40e_write_profile
54535610
* @hw: pointer to the hardware structure
@@ -5463,47 +5620,99 @@ i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
54635620
i40e_status status = 0;
54645621
struct i40e_section_table *sec_tbl;
54655622
struct i40e_profile_section_header *sec = NULL;
5466-
u32 dev_cnt;
5467-
u32 vendor_dev_id;
5468-
u32 *nvm;
5623+
struct i40e_profile_aq_section *ddp_aq;
54695624
u32 section_size = 0;
54705625
u32 offset = 0, info = 0;
5626+
u32 sec_off;
54715627
u32 i;
54725628

5473-
dev_cnt = profile->device_table_count;
5629+
status = i40e_validate_profile(hw, profile, track_id, false);
5630+
if (status)
5631+
return status;
54745632

5475-
for (i = 0; i < dev_cnt; i++) {
5476-
vendor_dev_id = profile->device_table[i].vendor_dev_id;
5477-
if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL)
5478-
if (hw->device_id == (vendor_dev_id & 0xFFFF))
5633+
I40E_SECTION_TABLE(profile, sec_tbl);
5634+
5635+
for (i = 0; i < sec_tbl->section_count; i++) {
5636+
sec_off = sec_tbl->section_offset[i];
5637+
sec = I40E_SECTION_HEADER(profile, sec_off);
5638+
/* Process generic admin command */
5639+
if (sec->section.type == SECTION_TYPE_AQ) {
5640+
ddp_aq = (struct i40e_profile_aq_section *)&sec[1];
5641+
status = i40e_ddp_exec_aq_section(hw, ddp_aq);
5642+
if (status) {
5643+
i40e_debug(hw, I40E_DEBUG_PACKAGE,
5644+
"Failed to execute aq: section %d, opcode %u\n",
5645+
i, ddp_aq->opcode);
54795646
break;
5647+
}
5648+
sec->section.type = SECTION_TYPE_RB_AQ;
5649+
}
5650+
5651+
/* Skip any non-mmio sections */
5652+
if (sec->section.type != SECTION_TYPE_MMIO)
5653+
continue;
5654+
5655+
section_size = sec->section.size +
5656+
sizeof(struct i40e_profile_section_header);
5657+
5658+
/* Write MMIO section */
5659+
status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
5660+
track_id, &offset, &info, NULL);
5661+
if (status) {
5662+
i40e_debug(hw, I40E_DEBUG_PACKAGE,
5663+
"Failed to write profile: section %d, offset %d, info %d\n",
5664+
i, offset, info);
5665+
break;
5666+
}
54805667
}
5481-
if (i == dev_cnt) {
5482-
i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support DDP");
5483-
return I40E_ERR_DEVICE_NOT_SUPPORTED;
5484-
}
5668+
return status;
5669+
}
5670+
5671+
/**
5672+
* i40e_rollback_profile
5673+
* @hw: pointer to the hardware structure
5674+
* @profile: pointer to the profile segment of the package to be removed
5675+
* @track_id: package tracking id
5676+
*
5677+
* Rolls back previously loaded package.
5678+
*/
5679+
enum i40e_status_code
5680+
i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
5681+
u32 track_id)
5682+
{
5683+
struct i40e_profile_section_header *sec = NULL;
5684+
i40e_status status = 0;
5685+
struct i40e_section_table *sec_tbl;
5686+
u32 offset = 0, info = 0;
5687+
u32 section_size = 0;
5688+
u32 sec_off;
5689+
int i;
54855690

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

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

5493-
/* Skip 'AQ', 'note' and 'name' sections */
5494-
if (sec->section.type != SECTION_TYPE_MMIO)
5697+
/* For rollback write sections in reverse */
5698+
for (i = sec_tbl->section_count - 1; i >= 0; i--) {
5699+
sec_off = sec_tbl->section_offset[i];
5700+
sec = I40E_SECTION_HEADER(profile, sec_off);
5701+
5702+
/* Skip any non-rollback sections */
5703+
if (sec->section.type != SECTION_TYPE_RB_MMIO)
54955704
continue;
54965705

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

5500-
/* Write profile */
5709+
/* Write roll-back MMIO section */
55015710
status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
55025711
track_id, &offset, &info, NULL);
55035712
if (status) {
55045713
i40e_debug(hw, I40E_DEBUG_PACKAGE,
5505-
"Failed to write profile: offset %d, info %d",
5506-
offset, info);
5714+
"Failed to write profile: section %d, offset %d, info %d\n",
5715+
i, offset, info);
55075716
break;
55085717
}
55095718
}

0 commit comments

Comments
 (0)