From 7a208e83fcedc0b845facc17d05ead0b9b73a967 Mon Sep 17 00:00:00 2001 From: Kamil Krawczyk Date: Wed, 4 Jun 2014 04:22:36 +0000 Subject: [PATCH 01/14] i40e: do not take NVM ownership for SR read We do not need to acquire NVM for Shadow RAM XSUM calculation, as we only read from SR through SRCTL register for which having the ownership is not required. Change-ID: Ie238a8f09917d1d25f24cc7cec271951ac7b98f2 Signed-off-by: Kamil Krawczyk Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 81299189a47d..66bcb15422da 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -324,13 +324,9 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, u16 checksum_sr = 0; u16 checksum_local = 0; - ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); - if (ret_code) - goto i40e_validate_nvm_checksum_exit; - ret_code = i40e_calc_nvm_checksum(hw, &checksum_local); if (ret_code) - goto i40e_validate_nvm_checksum_free; + goto i40e_validate_nvm_checksum_exit; /* Do not use i40e_read_nvm_word() because we do not want to take * the synchronization semaphores twice here. @@ -347,9 +343,6 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, if (checksum) *checksum = checksum_local; -i40e_validate_nvm_checksum_free: - i40e_release_nvm(hw); - i40e_validate_nvm_checksum_exit: return ret_code; } From 3ba3faeb62220411284551a6443395ce7960b17d Mon Sep 17 00:00:00 2001 From: Paul M Stillwell Jr Date: Wed, 4 Jun 2014 05:35:54 +0000 Subject: [PATCH 02/14] i40e/i40evf: Big endian fixes for handling HMC Fix HMC handling for big endian architectures. Change-ID: Id8c46fc341815d47bfe0af8b819f0ab9a1e9e515 Signed-off-by: Paul M Stillwell Jr Signed-off-by: Jeff Kirsher --- .../net/ethernet/intel/i40e/i40e_lan_hmc.c | 247 ++++++++++++++---- .../net/ethernet/intel/i40e/i40e_lan_hmc.h | 28 +- .../net/ethernet/intel/i40evf/i40e_lan_hmc.h | 28 +- 3 files changed, 236 insertions(+), 67 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c index 870ab1ee072c..5a603a5e9aa8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c @@ -746,6 +746,194 @@ static struct i40e_context_ele i40e_hmc_rxq_ce_info[] = { { 0 } }; +/** + * i40e_write_byte - replace HMC context byte + * @hmc_bits: pointer to the HMC memory + * @ce_info: a description of the struct to be read from + * @src: the struct to be read from + **/ +static void i40e_write_byte(u8 *hmc_bits, + struct i40e_context_ele *ce_info, + u8 *src) +{ + u8 src_byte, dest_byte, mask; + u8 *from, *dest; + u16 shift_width; + + /* copy from the next struct field */ + from = src + ce_info->offset; + + /* prepare the bits and mask */ + shift_width = ce_info->lsb % 8; + mask = ((u8)1 << ce_info->width) - 1; + + src_byte = *from; + src_byte &= mask; + + /* shift to correct alignment */ + mask <<= shift_width; + src_byte <<= shift_width; + + /* get the current bits from the target bit string */ + dest = hmc_bits + (ce_info->lsb / 8); + + memcpy(&dest_byte, dest, sizeof(dest_byte)); + + dest_byte &= ~mask; /* get the bits not changing */ + dest_byte |= src_byte; /* add in the new bits */ + + /* put it all back */ + memcpy(dest, &dest_byte, sizeof(dest_byte)); +} + +/** + * i40e_write_word - replace HMC context word + * @hmc_bits: pointer to the HMC memory + * @ce_info: a description of the struct to be read from + * @src: the struct to be read from + **/ +static void i40e_write_word(u8 *hmc_bits, + struct i40e_context_ele *ce_info, + u8 *src) +{ + u16 src_word, mask; + u8 *from, *dest; + u16 shift_width; + __le16 dest_word; + + /* copy from the next struct field */ + from = src + ce_info->offset; + + /* prepare the bits and mask */ + shift_width = ce_info->lsb % 8; + mask = ((u16)1 << ce_info->width) - 1; + + /* don't swizzle the bits until after the mask because the mask bits + * will be in a different bit position on big endian machines + */ + src_word = *(u16 *)from; + src_word &= mask; + + /* shift to correct alignment */ + mask <<= shift_width; + src_word <<= shift_width; + + /* get the current bits from the target bit string */ + dest = hmc_bits + (ce_info->lsb / 8); + + memcpy(&dest_word, dest, sizeof(dest_word)); + + dest_word &= ~(cpu_to_le16(mask)); /* get the bits not changing */ + dest_word |= cpu_to_le16(src_word); /* add in the new bits */ + + /* put it all back */ + memcpy(dest, &dest_word, sizeof(dest_word)); +} + +/** + * i40e_write_dword - replace HMC context dword + * @hmc_bits: pointer to the HMC memory + * @ce_info: a description of the struct to be read from + * @src: the struct to be read from + **/ +static void i40e_write_dword(u8 *hmc_bits, + struct i40e_context_ele *ce_info, + u8 *src) +{ + u32 src_dword, mask; + u8 *from, *dest; + u16 shift_width; + __le32 dest_dword; + + /* copy from the next struct field */ + from = src + ce_info->offset; + + /* prepare the bits and mask */ + shift_width = ce_info->lsb % 8; + + /* if the field width is exactly 32 on an x86 machine, then the shift + * operation will not work because the SHL instructions count is masked + * to 5 bits so the shift will do nothing + */ + if (ce_info->width < 32) + mask = ((u32)1 << ce_info->width) - 1; + else + mask = -1; + + /* don't swizzle the bits until after the mask because the mask bits + * will be in a different bit position on big endian machines + */ + src_dword = *(u32 *)from; + src_dword &= mask; + + /* shift to correct alignment */ + mask <<= shift_width; + src_dword <<= shift_width; + + /* get the current bits from the target bit string */ + dest = hmc_bits + (ce_info->lsb / 8); + + memcpy(&dest_dword, dest, sizeof(dest_dword)); + + dest_dword &= ~(cpu_to_le32(mask)); /* get the bits not changing */ + dest_dword |= cpu_to_le32(src_dword); /* add in the new bits */ + + /* put it all back */ + memcpy(dest, &dest_dword, sizeof(dest_dword)); +} + +/** + * i40e_write_qword - replace HMC context qword + * @hmc_bits: pointer to the HMC memory + * @ce_info: a description of the struct to be read from + * @src: the struct to be read from + **/ +static void i40e_write_qword(u8 *hmc_bits, + struct i40e_context_ele *ce_info, + u8 *src) +{ + u64 src_qword, mask; + u8 *from, *dest; + u16 shift_width; + __le64 dest_qword; + + /* copy from the next struct field */ + from = src + ce_info->offset; + + /* prepare the bits and mask */ + shift_width = ce_info->lsb % 8; + + /* if the field width is exactly 64 on an x86 machine, then the shift + * operation will not work because the SHL instructions count is masked + * to 6 bits so the shift will do nothing + */ + if (ce_info->width < 64) + mask = ((u64)1 << ce_info->width) - 1; + else + mask = -1; + + /* don't swizzle the bits until after the mask because the mask bits + * will be in a different bit position on big endian machines + */ + src_qword = *(u64 *)from; + src_qword &= mask; + + /* shift to correct alignment */ + mask <<= shift_width; + src_qword <<= shift_width; + + /* get the current bits from the target bit string */ + dest = hmc_bits + (ce_info->lsb / 8); + + memcpy(&dest_qword, dest, sizeof(dest_qword)); + + dest_qword &= ~(cpu_to_le64(mask)); /* get the bits not changing */ + dest_qword |= cpu_to_le64(src_qword); /* add in the new bits */ + + /* put it all back */ + memcpy(dest, &dest_qword, sizeof(dest_qword)); +} + /** * i40e_clear_hmc_context - zero out the HMC context bits * @hw: the hardware struct @@ -772,71 +960,28 @@ static i40e_status i40e_set_hmc_context(u8 *context_bytes, struct i40e_context_ele *ce_info, u8 *dest) { - u16 shift_width; - u64 bitfield; - u8 hi_byte; - u8 hi_mask; - u64 t_bits; - u64 mask; - u8 *p; int f; for (f = 0; ce_info[f].width != 0; f++) { - /* clear out the field */ - bitfield = 0; - /* copy from the next struct field */ - p = dest + ce_info[f].offset; + /* we have to deal with each element of the HMC using the + * correct size so that we are correct regardless of the + * endianness of the machine + */ switch (ce_info[f].size_of) { case 1: - bitfield = *p; + i40e_write_byte(context_bytes, &ce_info[f], dest); break; case 2: - bitfield = cpu_to_le16(*(u16 *)p); + i40e_write_word(context_bytes, &ce_info[f], dest); break; case 4: - bitfield = cpu_to_le32(*(u32 *)p); + i40e_write_dword(context_bytes, &ce_info[f], dest); break; case 8: - bitfield = cpu_to_le64(*(u64 *)p); + i40e_write_qword(context_bytes, &ce_info[f], dest); break; } - - /* prepare the bits and mask */ - shift_width = ce_info[f].lsb % 8; - mask = ((u64)1 << ce_info[f].width) - 1; - - /* save upper bytes for special case */ - hi_mask = (u8)((mask >> 56) & 0xff); - hi_byte = (u8)((bitfield >> 56) & 0xff); - - /* shift to correct alignment */ - mask <<= shift_width; - bitfield <<= shift_width; - - /* get the current bits from the target bit string */ - p = context_bytes + (ce_info[f].lsb / 8); - memcpy(&t_bits, p, sizeof(u64)); - - t_bits &= ~mask; /* get the bits not changing */ - t_bits |= bitfield; /* add in the new bits */ - - /* put it all back */ - memcpy(p, &t_bits, sizeof(u64)); - - /* deal with the special case if needed - * example: 62 bit field that starts in bit 5 of first byte - * will overlap 3 bits into byte 9 - */ - if ((shift_width + ce_info[f].width) > 64) { - u8 byte; - - hi_mask >>= (8 - shift_width); - hi_byte >>= (8 - shift_width); - byte = p[8] & ~hi_mask; /* get the bits not changing */ - byte |= hi_byte; /* add in the new bits */ - p[8] = byte; /* put it back */ - } } return 0; diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h index eb65fe23c4a7..e74128db5be5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h @@ -32,16 +32,22 @@ struct i40e_hw; /* HMC element context information */ -/* Rx queue context data */ +/* Rx queue context data + * + * The sizes of the variables may be larger than needed due to crossing byte + * boundaries. If we do not have the width of the variable set to the correct + * size then we could end up shifting bits off the top of the variable when the + * variable is at the top of a byte and crosses over into the next byte. + */ struct i40e_hmc_obj_rxq { u16 head; - u8 cpuid; + u16 cpuid; /* bigger than needed, see above for reason */ u64 base; u16 qlen; #define I40E_RXQ_CTX_DBUFF_SHIFT 7 - u8 dbuff; + u16 dbuff; /* bigger than needed, see above for reason */ #define I40E_RXQ_CTX_HBUFF_SHIFT 6 - u8 hbuff; + u16 hbuff; /* bigger than needed, see above for reason */ u8 dtype; u8 dsize; u8 crcstrip; @@ -50,16 +56,22 @@ struct i40e_hmc_obj_rxq { u8 hsplit_0; u8 hsplit_1; u8 showiv; - u16 rxmax; + u32 rxmax; /* bigger than needed, see above for reason */ u8 tphrdesc_ena; u8 tphwdesc_ena; u8 tphdata_ena; u8 tphhead_ena; - u8 lrxqthresh; + u16 lrxqthresh; /* bigger than needed, see above for reason */ u8 prefena; /* NOTE: normally must be set to 1 at init */ }; -/* Tx queue context data */ +/* Tx queue context data +* +* The sizes of the variables may be larger than needed due to crossing byte +* boundaries. If we do not have the width of the variable set to the correct +* size then we could end up shifting bits off the top of the variable when the +* variable is at the top of a byte and crosses over into the next byte. +*/ struct i40e_hmc_obj_txq { u16 head; u8 new_context; @@ -69,7 +81,7 @@ struct i40e_hmc_obj_txq { u8 fd_ena; u8 alt_vlan_ena; u16 thead_wb; - u16 cpuid; + u8 cpuid; u8 head_wb_ena; u16 qlen; u8 tphrdesc_ena; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h index d6f762241537..a5d79877354c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h @@ -32,16 +32,22 @@ struct i40e_hw; /* HMC element context information */ -/* Rx queue context data */ +/* Rx queue context data + * + * The sizes of the variables may be larger than needed due to crossing byte + * boundaries. If we do not have the width of the variable set to the correct + * size then we could end up shifting bits off the top of the variable when the + * variable is at the top of a byte and crosses over into the next byte. + */ struct i40e_hmc_obj_rxq { u16 head; - u8 cpuid; + u16 cpuid; /* bigger than needed, see above for reason */ u64 base; u16 qlen; #define I40E_RXQ_CTX_DBUFF_SHIFT 7 - u8 dbuff; + u16 dbuff; /* bigger than needed, see above for reason */ #define I40E_RXQ_CTX_HBUFF_SHIFT 6 - u8 hbuff; + u16 hbuff; /* bigger than needed, see above for reason */ u8 dtype; u8 dsize; u8 crcstrip; @@ -50,16 +56,22 @@ struct i40e_hmc_obj_rxq { u8 hsplit_0; u8 hsplit_1; u8 showiv; - u16 rxmax; + u32 rxmax; /* bigger than needed, see above for reason */ u8 tphrdesc_ena; u8 tphwdesc_ena; u8 tphdata_ena; u8 tphhead_ena; - u8 lrxqthresh; + u16 lrxqthresh; /* bigger than needed, see above for reason */ u8 prefena; /* NOTE: normally must be set to 1 at init */ }; -/* Tx queue context data */ +/* Tx queue context data +* +* The sizes of the variables may be larger than needed due to crossing byte +* boundaries. If we do not have the width of the variable set to the correct +* size then we could end up shifting bits off the top of the variable when the +* variable is at the top of a byte and crosses over into the next byte. +*/ struct i40e_hmc_obj_txq { u16 head; u8 new_context; @@ -69,7 +81,7 @@ struct i40e_hmc_obj_txq { u8 fd_ena; u8 alt_vlan_ena; u16 thead_wb; - u16 cpuid; + u8 cpuid; u8 head_wb_ena; u16 qlen; u8 tphrdesc_ena; From e5d17c3ed2cf0485c6b85d783e1d20a8cfad93c1 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Wed, 4 Jun 2014 04:22:38 +0000 Subject: [PATCH 03/14] i40evf: don't stop watchdog if it hasn't started If the VF driver fails to complete early init, then rmmod can cause a softlock when the driver tries to stop a watchdog timer that never even got initialized. Add a check to see if the timer is actually initialized before stopping it. Change-ID: Id9d550aa8838e07f4b02afe7bc017ef983779efc Signed-off-by: Mitch Williams Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index b473172708f5..36b1ad76a8bf 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2408,7 +2408,9 @@ static void i40evf_remove(struct pci_dev *pdev) i40evf_reset_interrupt_capability(adapter); } - del_timer_sync(&adapter->watchdog_timer); + if (adapter->watchdog_timer.function) + del_timer_sync(&adapter->watchdog_timer); + flush_scheduled_work(); if (hw->aq.asq.count) From 6a8e93db98e36b5b3b8c10b9adae073d6bd1062a Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 5 Jun 2014 00:09:17 +0000 Subject: [PATCH 04/14] i40evf: return more useful error information When verifying the API version (which is the first time the driver communicates with the firmware and thus the PF driver), there are many ways in which a failure can occur. There may be an error from the firmware, there may be unresponsive firmware, there may be an error from the PF driver, etc, etc. Make this function return more useful information, instead of just -EIO. Propagate FW errors back to the caller, and log a message if the PF sends an invalid reply. Change-ID: I3e9135a2b80f7acdb855f62f12b2b2668c9a8951 Signed-off-by: Mitch Williams Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 2dc0bac76717..0ed2ad7e4eee 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -80,8 +80,9 @@ int i40evf_send_api_ver(struct i40evf_adapter *adapter) * @adapter: adapter structure * * Compare API versions with the PF. Must be called after admin queue is - * initialized. Returns 0 if API versions match, -EIO if - * they do not, or I40E_ERR_ADMIN_QUEUE_NO_WORK if the admin queue is empty. + * initialized. Returns 0 if API versions match, -EIO if they do not, + * I40E_ERR_ADMIN_QUEUE_NO_WORK if the admin queue is empty, and any errors + * from the firmware are propagated. **/ int i40evf_verify_api_ver(struct i40evf_adapter *adapter) { @@ -102,13 +103,13 @@ int i40evf_verify_api_ver(struct i40evf_adapter *adapter) goto out_alloc; err = (i40e_status)le32_to_cpu(event.desc.cookie_low); - if (err) { - err = -EIO; + if (err) goto out_alloc; - } if ((enum i40e_virtchnl_ops)le32_to_cpu(event.desc.cookie_high) != I40E_VIRTCHNL_OP_VERSION) { + dev_info(&adapter->pdev->dev, "Invalid reply type %d from PF\n", + le32_to_cpu(event.desc.cookie_high)); err = -EIO; goto out_alloc; } From 3f2ab1721f77935eb4ee50b78c6ecfe24b3a2648 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Wed, 4 Jun 2014 04:22:40 +0000 Subject: [PATCH 05/14] i40evf: fix typo Correct a missing word in a log message. Change-ID: Id94da7d9f842382d073b3947e0b616503e2f8e91 Signed-off-by: Mitch Williams Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 36b1ad76a8bf..7138ab483625 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2022,7 +2022,7 @@ static void i40evf_init_task(struct work_struct *work) } err = i40evf_send_vf_config_msg(adapter); if (err) { - dev_err(&pdev->dev, "Unable send config request (%d)\n", + dev_err(&pdev->dev, "Unable to send config request (%d)\n", err); goto err; } From 56f9920a952130a7cd20952b3525bc238a87e489 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Wed, 4 Jun 2014 04:22:41 +0000 Subject: [PATCH 06/14] i40evf: resend FW request if no response Sometimes the firmware will not indicate an error but fail to pass a message between the VF and the PF driver. If this happens, just resend the request. This fixes an initialization failure if many VFs are instantiated at the same time and the VF module is autoloaded. Change-ID: Idd1ad8da2fd5137859244685c355941427d317d7 Signed-off-by: Mitch Williams Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 7138ab483625..8082a9fa5d10 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2018,6 +2018,10 @@ static void i40evf_init_task(struct work_struct *work) if (err) { dev_info(&pdev->dev, "Unable to verify API version (%d), retrying\n", err); + if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) { + dev_info(&pdev->dev, "Resending request\n"); + err = i40evf_send_api_ver(adapter); + } goto err; } err = i40evf_send_vf_config_msg(adapter); From 69d1a70c3f0046d06f5520a230eee9a829ee6689 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 4 Jun 2014 04:22:42 +0000 Subject: [PATCH 07/14] i40e: rename i40e_ptp_enable to i40e_ptp_feature_enable Reduces possible confusion and ambiguity in purpose of the ancillary feature control entry point function. Change-ID: I21d773c1a86878f6d061505185b596c788d1b7cc Signed-off-by: Jacob Keller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 101f439acda6..6f7d73b6affb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -216,7 +216,7 @@ static int i40e_ptp_settime(struct ptp_clock_info *ptp, } /** - * i40e_ptp_enable - Enable/disable ancillary features of the PHC subsystem + * i40e_ptp_feature_enable - Enable/disable ancillary features of the PHC subsystem * @ptp: The PTP clock structure * @rq: The requested feature to change * @on: Enable/disable flag @@ -224,8 +224,8 @@ static int i40e_ptp_settime(struct ptp_clock_info *ptp, * The XL710 does not support any of the ancillary features of the PHC * subsystem, so this function may just return. **/ -static int i40e_ptp_enable(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, int on) +static int i40e_ptp_feature_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) { return -EOPNOTSUPP; } @@ -560,7 +560,7 @@ void i40e_ptp_init(struct i40e_pf *pf) pf->ptp_caps.adjtime = i40e_ptp_adjtime; pf->ptp_caps.gettime = i40e_ptp_gettime; pf->ptp_caps.settime = i40e_ptp_settime; - pf->ptp_caps.enable = i40e_ptp_enable; + pf->ptp_caps.enable = i40e_ptp_feature_enable; /* Attempt to register the clock before enabling the hardware. */ pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev); From 189464555a4aef4db07f90294bd3723079f7c19a Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 4 Jun 2014 06:08:29 +0000 Subject: [PATCH 08/14] i40e: break PTP hardware control from ioctl command for timestamp mode This patch facilitates future work by breaking the PTP hardware control bits out of the i40e_set_ts_config function. By doing this, we can maintain state about the 1588 timestamping mode and properly re-enable to the last known mode during a re-initialize of 1588 bits. This patch also modifies i40e_ptp_init to call the i40e_ptp_set_timestamp_mode during the reconfiguration process. A future patch will ensure that the hwtstamp_config structure is not reset during this process, so that timestamp mode will be maintained across a reset. Change-ID: Ic20832c96c5c512ac203b6c7534e10d891c560f0 Signed-off-by: Jacob Keller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 59 ++++++++++++++++------ 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 6f7d73b6affb..e5f558c9d7d0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -423,28 +423,23 @@ int i40e_ptp_get_ts_config(struct i40e_pf *pf, struct ifreq *ifr) } /** - * i40e_ptp_set_ts_config - ioctl interface to control the HW timestamping + * i40e_ptp_set_timestamp_mode - setup hardware for requested timestamp mode * @pf: Board private structure - * @ifreq: ioctl data + * @config: hwtstamp settings requested or saved * - * Respond to the user filter requests and make the appropriate hardware - * changes here. The XL710 cannot support splitting of the Tx/Rx timestamping - * logic, so keep track in software of whether to indicate these timestamps - * or not. + * Control hardware registers to enter the specific mode requested by the + * user. Also used during reset path to ensure that timestamp settings are + * maintained. * - * It is permissible to "upgrade" the user request to a broader filter, as long - * as the user receives the timestamps they care about and the user is notified - * the filter has been broadened. + * Note: modifies config in place, and may update the requested mode to be + * more broad if the specific filter is not directly supported. **/ -int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr) +static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, + struct hwtstamp_config *config) { struct i40e_hw *hw = &pf->hw; - struct hwtstamp_config *config = &pf->tstamp_config; u32 pf_id, tsyntype, regval; - if (copy_from_user(config, ifr->ifr_data, sizeof(*config))) - return -EFAULT; - /* Reserved for future extensions. */ if (config->flags) return -EINVAL; @@ -535,6 +530,35 @@ int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr) wr32(hw, I40E_PRTTSYN_CTL1, regval); } + return 0; +} + +/** + * i40e_ptp_set_ts_config - ioctl interface to control the HW timestamping + * @pf: Board private structure + * @ifreq: ioctl data + * + * Respond to the user filter requests and make the appropriate hardware + * changes here. The XL710 cannot support splitting of the Tx/Rx timestamping + * logic, so keep track in software of whether to indicate these timestamps + * or not. + * + * It is permissible to "upgrade" the user request to a broader filter, as long + * as the user receives the timestamps they care about and the user is notified + * the filter has been broadened. + **/ +int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr) +{ + struct hwtstamp_config *config = &pf->tstamp_config; + int err; + + if (copy_from_user(config, ifr->ifr_data, sizeof(*config))) + return -EFAULT; + + err = i40e_ptp_set_timestamp_mode(pf, config); + if (err) + return err; + return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? -EFAULT : 0; } @@ -578,6 +602,9 @@ void i40e_ptp_init(struct i40e_pf *pf) netdev->name); pf->flags |= I40E_FLAG_PTP; + pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; + pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF; + /* Ensure the clocks are running. */ regval = rd32(hw, I40E_PRTTSYN_CTL0); regval |= I40E_PRTTSYN_CTL0_TSYNENA_MASK; @@ -589,8 +616,8 @@ void i40e_ptp_init(struct i40e_pf *pf) /* Set the increment value per clock tick. */ i40e_ptp_set_increment(pf); - /* reset the tstamp_config */ - memset(&pf->tstamp_config, 0, sizeof(pf->tstamp_config)); + /* reset timestamping mode */ + i40e_ptp_set_timestamp_mode(pf, &pf->tstamp_config); /* Set the clock value. */ ts = ktime_to_timespec(ktime_get_real()); From d19af2afe70c11c17552e3290560037a8812f467 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 4 Jun 2014 04:22:44 +0000 Subject: [PATCH 09/14] i40e: don't store user requested mode until we've validated it This patch prevents the SIOCGHWTSTAMP ioctl from possibly returning bad data, by not permanently storing the setting into the private structure until after we've finished validating that we can support it. Change-ID: Ib59f9b4f73f451d5a2e76fb8efa5d4271b218433 Signed-off-by: Jacob Keller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index e5f558c9d7d0..f7dded9976e9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -549,17 +549,20 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, **/ int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr) { - struct hwtstamp_config *config = &pf->tstamp_config; + struct hwtstamp_config config; int err; - if (copy_from_user(config, ifr->ifr_data, sizeof(*config))) + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) return -EFAULT; - err = i40e_ptp_set_timestamp_mode(pf, config); + err = i40e_ptp_set_timestamp_mode(pf, &config); if (err) return err; - return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? + /* save these settings for future reference */ + pf->tstamp_config = config; + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; } From fbd5e2df9f9e65439f41953455e3eb5b9aab3685 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 4 Jun 2014 04:22:45 +0000 Subject: [PATCH 10/14] i40e: only create PTP device node once Currently every time we run through the i40e_ptp_init routine, we create a new device node. This function is called by i40e_reset_and_rebuild which is used to handle reset of the device. Even though the 1588 registers only get cleared on a GLOBAL reset, this function is still called to handle a CORE reset. This causes a leak of PTP device nodes at every reset. To fix this, break PTP device clock node creation out of i40e_ptp_init, and only call this if we don't already have a device created. Further invocation of i40e_ptp_init will not generate new PTP devices. Instead, only the necessary work required to reconfigure 1588 will be done. This change also fixes an issue where a reset can cause the device to forget it's timestamp configuration, and revert to the default mode. Change-ID: I741d01c61d9fe1d24887859d1316e1a8a892909e Signed-off-by: Jacob Keller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 58 +++++++++++++++++----- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index f7dded9976e9..0c935e8f3a6c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -567,18 +567,22 @@ int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr) } /** - * i40e_ptp_init - Initialize the 1588 support and register the PHC + * i40e_ptp_create_clock - Create PTP clock device for userspace * @pf: Board private structure * - * This function registers the device clock as a PHC. If it is successful, it - * starts the clock in the hardware. + * This function creates a new PTP clock device. It only creates one if we + * don't already have one, so it is safe to call. Will return error if it + * can't create one, but success if we already have a device. Should be used + * by i40e_ptp_init to create clock initially, and prevent global resets from + * creating new clock devices. **/ -void i40e_ptp_init(struct i40e_pf *pf) +static long i40e_ptp_create_clock(struct i40e_pf *pf) { - struct i40e_hw *hw = &pf->hw; - struct net_device *netdev = pf->vsi[pf->lan_vsi]->netdev; + /* no need to create a clock device if we already have one */ + if (!IS_ERR_OR_NULL(pf->ptp_clock)) + return 0; - strncpy(pf->ptp_caps.name, "i40e", sizeof(pf->ptp_caps.name)); + strncpy(pf->ptp_caps.name, i40e_driver_name, sizeof(pf->ptp_caps.name)); pf->ptp_caps.owner = THIS_MODULE; pf->ptp_caps.max_adj = 999999999; pf->ptp_caps.n_ext_ts = 0; @@ -592,6 +596,41 @@ void i40e_ptp_init(struct i40e_pf *pf) /* Attempt to register the clock before enabling the hardware. */ pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev); if (IS_ERR(pf->ptp_clock)) { + return PTR_ERR(pf->ptp_clock); + } + + /* clear the hwtstamp settings here during clock create, instead of + * during regular init, so that we can maintain settings across a + * reset or suspend. + */ + pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; + pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF; + + return 0; +} + +/** + * i40e_ptp_init - Initialize the 1588 support after device probe or reset + * @pf: Board private structure + * + * This function sets device up for 1588 support. The first time it is run, it + * will create a PHC clock device. It does not create a clock device if one + * already exists. It also reconfigures the device after a reset. + **/ +void i40e_ptp_init(struct i40e_pf *pf) +{ + struct net_device *netdev = pf->vsi[pf->lan_vsi]->netdev; + struct i40e_hw *hw = &pf->hw; + long err; + + /* we have to initialize the lock first, since we can't control + * when the user will enter the PHC device entry points + */ + spin_lock_init(&pf->tmreg_lock); + + /* ensure we have a clock device */ + err = i40e_ptp_create_clock(pf); + if (err) { pf->ptp_clock = NULL; dev_err(&pf->pdev->dev, "%s: ptp_clock_register failed\n", __func__); @@ -599,15 +638,10 @@ void i40e_ptp_init(struct i40e_pf *pf) struct timespec ts; u32 regval; - spin_lock_init(&pf->tmreg_lock); - dev_info(&pf->pdev->dev, "%s: added PHC on %s\n", __func__, netdev->name); pf->flags |= I40E_FLAG_PTP; - pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; - pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF; - /* Ensure the clocks are running. */ regval = rd32(hw, I40E_PRTTSYN_CTL0); regval |= I40E_PRTTSYN_CTL0_TSYNENA_MASK; From 12be846ddda83b9f641be6cdbfd6891594fda4ec Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Wed, 4 Jun 2014 04:22:46 +0000 Subject: [PATCH 11/14] i40e: Avoid adding the TCP-IPv4 filter twice There wasn't a need to play the logic twice, it seems like a left over from when we had to add two PTYPEs for one filter. There should be no change in the number of filters that actually got added to the hardware. Change-ID: I5071d02eafd020b60e30eb96219f110f334eec85 Signed-off-by: Anjali Singhai Jain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index e49f31dbd5d8..4d96b743f991 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -271,19 +271,6 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, fd_data->pctype, ret); } - fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; - - ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); - if (ret) { - dev_info(&pf->pdev->dev, - "Filter command send failed for PCTYPE %d (ret = %d)\n", - fd_data->pctype, ret); - err = true; - } else { - dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n", - fd_data->pctype, ret); - } - return err ? -EOPNOTSUPP : 0; } From 129573883c5b39d978c4a7fbe513f8e54898e27e Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Wed, 4 Jun 2014 04:22:47 +0000 Subject: [PATCH 12/14] i40e: Fix the FD sideband logic to detect a FD table full condition Hardware does not have a way of telling a PF how much of the global shared FD table space is still available or is consumed. Previously, every PF but PF0 would think there was still space available when there wasn't. The PFs would continue to try to add filters and fail. With this new logic if a filter programming error is detected we just check if we are close to the guaranteed space full and that can be used as a hint to say, there might not be space and we should turn off the features. This way we can turn off the feature in SW for all PFs in time. Change-ID: I725cb2fab16c033f883056362b4542c1400503c5 Signed-off-by: Anjali Singhai Jain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 3 ++- drivers/net/ethernet/intel/i40e/i40e_main.c | 20 ++++++++++++++++---- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 4 ++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 2ec6e8a11ee1..0fbb32a8ad42 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -154,7 +154,7 @@ struct i40e_lump_tracking { #define I40E_DEFAULT_ATR_SAMPLE_RATE 20 #define I40E_FDIR_MAX_RAW_PACKET_SIZE 512 #define I40E_FDIR_BUFFER_FULL_MARGIN 10 -#define I40E_FDIR_BUFFER_HEAD_ROOM 200 +#define I40E_FDIR_BUFFER_HEAD_ROOM 32 enum i40e_fd_stat_idx { I40E_FD_STAT_ATR, @@ -582,6 +582,7 @@ int i40e_add_del_fdir(struct i40e_vsi *vsi, struct i40e_fdir_filter *input, bool add); void i40e_fdir_check_and_reenable(struct i40e_pf *pf); int i40e_get_current_fd_count(struct i40e_pf *pf); +int i40e_get_cur_guaranteed_fd_count(struct i40e_pf *pf); bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features); void i40e_set_ethtool_ops(struct net_device *netdev); struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5980d6b3fb1a..17b1295bf35a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4944,7 +4944,20 @@ static void i40e_service_event_complete(struct i40e_pf *pf) } /** - * i40e_get_current_fd_count - Get the count of FD filters programmed in the HW + * i40e_get_cur_guaranteed_fd_count - Get the consumed guaranteed FD filters + * @pf: board private structure + **/ +int i40e_get_cur_guaranteed_fd_count(struct i40e_pf *pf) +{ + int val, fcnt_prog; + + val = rd32(&pf->hw, I40E_PFQF_FDSTAT); + fcnt_prog = (val & I40E_PFQF_FDSTAT_GUARANT_CNT_MASK); + return fcnt_prog; +} + +/** + * i40e_get_current_fd_count - Get the count of total FD filters programmed * @pf: board private structure **/ int i40e_get_current_fd_count(struct i40e_pf *pf) @@ -4956,7 +4969,6 @@ int i40e_get_current_fd_count(struct i40e_pf *pf) I40E_PFQF_FDSTAT_BEST_CNT_SHIFT); return fcnt_prog; } - /** * i40e_fdir_check_and_reenable - Function to reenabe FD ATR or SB if disabled * @pf: board private structure @@ -4971,8 +4983,8 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && (pf->flags & I40E_FLAG_FD_SB_ENABLED)) return; - fcnt_prog = i40e_get_current_fd_count(pf); - fcnt_avail = i40e_get_fd_cnt_all(pf); + fcnt_prog = i40e_get_cur_guaranteed_fd_count(pf); + fcnt_avail = pf->fdir_pf_filter_count; if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) { if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 4d96b743f991..c5749d6526ea 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -437,8 +437,8 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, rx_desc->wb.qword0.hi_dword.fd_id); /* filter programming failed most likely due to table full */ - fcnt_prog = i40e_get_current_fd_count(pf); - fcnt_avail = i40e_get_fd_cnt_all(pf); + fcnt_prog = i40e_get_cur_guaranteed_fd_count(pf); + fcnt_avail = pf->fdir_pf_filter_count; /* If ATR is running fcnt_prog can quickly change, * if we are very close to full, it makes sense to disable * FD ATR/SB and then re-enable it when there is room. From e17ff05c5d9ee64550030d03e63719e6dc62d729 Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Wed, 4 Jun 2014 04:22:48 +0000 Subject: [PATCH 13/14] i40e: Add debugfs hooks to print current total FD filter count "fd current cnt" can be used to print the total filters consumed by this interface, this includes guaranteed and best effort filters. Change-ID: I2c417810c4999ce1388d2ea26f8e69679ba33966 Signed-off-by: Anjali Singhai Jain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 910b01b90cdc..ec07332e109e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -1743,6 +1743,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, false); } else if (strncmp(cmd_buf, "fd-atr on", 9) == 0) { i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, true); + } else if (strncmp(cmd_buf, "fd current cnt", 14) == 0) { + dev_info(&pf->pdev->dev, "FD current total filter count for this interface: %d\n", + i40e_get_current_fd_count(pf)); } else if (strncmp(cmd_buf, "lldp", 4) == 0) { if (strncmp(&cmd_buf[5], "stop", 4) == 0) { int ret; @@ -1962,6 +1965,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, " rem fd_filter \n"); dev_info(&pf->pdev->dev, " fd-atr off\n"); dev_info(&pf->pdev->dev, " fd-atr on\n"); + dev_info(&pf->pdev->dev, " fd current cnt"); dev_info(&pf->pdev->dev, " lldp start\n"); dev_info(&pf->pdev->dev, " lldp stop\n"); dev_info(&pf->pdev->dev, " lldp get local\n"); From 99753ea60626a4c5059b24e2a4c22f8fd88b8207 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Wed, 4 Jun 2014 04:22:49 +0000 Subject: [PATCH 14/14] i40e: fix fdir programming There were a couple of fields in the fdir descriptor setup that were not being reprogrammed, which left the opportunity for stale data to be pushed as part of the descriptor next time it was used. Change-ID: Ieee5c96a7d4713d469693f086c4854de949a7633 Signed-off-by: Jesse Brandeburg Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index c5749d6526ea..5cc27fba8ad5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -100,8 +100,6 @@ int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet, I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT) & I40E_TXD_FLTR_QW0_DEST_VSI_MASK; - fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(fpt); - dcc = I40E_TX_DESC_DTYPE_FILTER_PROG; if (add) @@ -124,6 +122,8 @@ int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet, I40E_TXD_FLTR_QW1_CNTINDEX_MASK; } + fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(fpt); + fdir_desc->rsvd = cpu_to_le32(0); fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dcc); fdir_desc->fd_id = cpu_to_le32(fdir_data->fd_id); @@ -1688,7 +1688,9 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, I40E_TXD_FLTR_QW1_CNTINDEX_MASK; fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(flex_ptype); + fdir_desc->rsvd = cpu_to_le32(0); fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dtype_cmd); + fdir_desc->fd_id = cpu_to_le32(0); } /**