aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnjali Singhai Jain <anjali.singhai@intel.com>2017-09-01 16:42:49 -0400
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>2017-09-05 20:48:22 -0400
commit09f79fd49d94cda5837e9bfd0cb222232b3b6d9f (patch)
tree88978497482700e86d4b6dc70bbdb7d5c421ac5f
parent6d9c153a0b84392406bc77600aa7d3ea365de041 (diff)
i40e: avoid NVM acquire deadlock during NVM update
X722 devices use the AdminQ to access the NVM, and this requires taking the AdminQ lock. Because of this, we lock the AdminQ during i40e_read_nvm(), which is also called in places where the lock is already held, such as the firmware update path which wants to lock once and then unlock when finished after performing several tasks. Although this should have only affected X722 devices, commit 96a39aed25e6 ("i40e: Acquire NVM lock before reads on all devices", 2016-12-02) added locking for all NVM reads, regardless of device family. This resulted in us accidentally causing NVM acquire timeouts on all devices, causing failed firmware updates which left the eeprom in a corrupt state. Create unsafe non-locked variants of i40e_read_nvm_word and i40e_read_nvm_buffer, __i40e_read_nvm_word and __i40e_read_nvm_buffer respectively. These variants will not take the NVM lock and are expected to only be called in places where the NVM lock is already held if needed. Since the only caller of i40e_read_nvm_buffer() was in such a path, remove it entirely in favor of the unsafe version. If necessary we can always add it back in the future. Additionally, we now need to hold the NVM lock in i40e_validate_checksum because the call to i40e_calc_nvm_checksum now assumes that the NVM lock is held. We can further move the call to read I40E_SR_SW_CHECKSUM_WORD up a bit so that we do not need to acquire the NVM lock twice. This should resolve firmware updates and also fix potential raise that could have caused the driver to report an invalid NVM checksum upon driver load. Reported-by: Stefan Assmann <sassmann@kpanic.de> Fixes: 96a39aed25e6 ("i40e: Acquire NVM lock before reads on all devices", 2016-12-02) Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Tested-by: Andrew Bowers <andrewx.bowers@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_nvm.c98
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_prototype.h2
2 files changed, 60 insertions, 40 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
index 800bd55d0159..154ba49c2c6f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
@@ -266,7 +266,7 @@ static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, u8 module_pointer,
266 * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) 266 * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
267 * @data: word read from the Shadow RAM 267 * @data: word read from the Shadow RAM
268 * 268 *
269 * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. 269 * Reads one 16 bit word from the Shadow RAM using the AdminQ
270 **/ 270 **/
271static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, 271static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset,
272 u16 *data) 272 u16 *data)
@@ -280,27 +280,49 @@ static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset,
280} 280}
281 281
282/** 282/**
283 * i40e_read_nvm_word - Reads Shadow RAM 283 * __i40e_read_nvm_word - Reads nvm word, assumes called does the locking
284 * @hw: pointer to the HW structure 284 * @hw: pointer to the HW structure
285 * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) 285 * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
286 * @data: word read from the Shadow RAM 286 * @data: word read from the Shadow RAM
287 * 287 *
288 * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. 288 * Reads one 16 bit word from the Shadow RAM.
289 *
290 * Do not use this function except in cases where the nvm lock is already
291 * taken via i40e_acquire_nvm().
292 **/
293static i40e_status __i40e_read_nvm_word(struct i40e_hw *hw,
294 u16 offset, u16 *data)
295{
296 i40e_status ret_code = 0;
297
298 if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE)
299 ret_code = i40e_read_nvm_word_aq(hw, offset, data);
300 else
301 ret_code = i40e_read_nvm_word_srctl(hw, offset, data);
302 return ret_code;
303}
304
305/**
306 * i40e_read_nvm_word - Reads nvm word and acquire lock if necessary
307 * @hw: pointer to the HW structure
308 * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
309 * @data: word read from the Shadow RAM
310 *
311 * Reads one 16 bit word from the Shadow RAM.
289 **/ 312 **/
290i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, 313i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
291 u16 *data) 314 u16 *data)
292{ 315{
293 enum i40e_status_code ret_code = 0; 316 i40e_status ret_code = 0;
294 317
295 ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); 318 ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
296 if (!ret_code) { 319 if (ret_code)
297 if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { 320 return ret_code;
298 ret_code = i40e_read_nvm_word_aq(hw, offset, data); 321
299 } else { 322 ret_code = __i40e_read_nvm_word(hw, offset, data);
300 ret_code = i40e_read_nvm_word_srctl(hw, offset, data); 323
301 } 324 i40e_release_nvm(hw);
302 i40e_release_nvm(hw); 325
303 }
304 return ret_code; 326 return ret_code;
305} 327}
306 328
@@ -393,31 +415,25 @@ read_nvm_buffer_aq_exit:
393} 415}
394 416
395/** 417/**
396 * i40e_read_nvm_buffer - Reads Shadow RAM buffer 418 * __i40e_read_nvm_buffer - Reads nvm buffer, caller must acquire lock
397 * @hw: pointer to the HW structure 419 * @hw: pointer to the HW structure
398 * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). 420 * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
399 * @words: (in) number of words to read; (out) number of words actually read 421 * @words: (in) number of words to read; (out) number of words actually read
400 * @data: words read from the Shadow RAM 422 * @data: words read from the Shadow RAM
401 * 423 *
402 * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() 424 * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd()
403 * method. The buffer read is preceded by the NVM ownership take 425 * method.
404 * and followed by the release.
405 **/ 426 **/
406i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, 427static i40e_status __i40e_read_nvm_buffer(struct i40e_hw *hw,
407 u16 *words, u16 *data) 428 u16 offset, u16 *words,
429 u16 *data)
408{ 430{
409 enum i40e_status_code ret_code = 0; 431 i40e_status ret_code = 0;
410 432
411 if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { 433 if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE)
412 ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); 434 ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, data);
413 if (!ret_code) { 435 else
414 ret_code = i40e_read_nvm_buffer_aq(hw, offset, words,
415 data);
416 i40e_release_nvm(hw);
417 }
418 } else {
419 ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); 436 ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data);
420 }
421 return ret_code; 437 return ret_code;
422} 438}
423 439
@@ -499,15 +515,15 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw,
499 data = (u16 *)vmem.va; 515 data = (u16 *)vmem.va;
500 516
501 /* read pointer to VPD area */ 517 /* read pointer to VPD area */
502 ret_code = i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); 518 ret_code = __i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module);
503 if (ret_code) { 519 if (ret_code) {
504 ret_code = I40E_ERR_NVM_CHECKSUM; 520 ret_code = I40E_ERR_NVM_CHECKSUM;
505 goto i40e_calc_nvm_checksum_exit; 521 goto i40e_calc_nvm_checksum_exit;
506 } 522 }
507 523
508 /* read pointer to PCIe Alt Auto-load module */ 524 /* read pointer to PCIe Alt Auto-load module */
509 ret_code = i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, 525 ret_code = __i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR,
510 &pcie_alt_module); 526 &pcie_alt_module);
511 if (ret_code) { 527 if (ret_code) {
512 ret_code = I40E_ERR_NVM_CHECKSUM; 528 ret_code = I40E_ERR_NVM_CHECKSUM;
513 goto i40e_calc_nvm_checksum_exit; 529 goto i40e_calc_nvm_checksum_exit;
@@ -521,7 +537,7 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw,
521 if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) { 537 if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) {
522 u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS; 538 u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS;
523 539
524 ret_code = i40e_read_nvm_buffer(hw, i, &words, data); 540 ret_code = __i40e_read_nvm_buffer(hw, i, &words, data);
525 if (ret_code) { 541 if (ret_code) {
526 ret_code = I40E_ERR_NVM_CHECKSUM; 542 ret_code = I40E_ERR_NVM_CHECKSUM;
527 goto i40e_calc_nvm_checksum_exit; 543 goto i40e_calc_nvm_checksum_exit;
@@ -593,14 +609,19 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw,
593 u16 checksum_sr = 0; 609 u16 checksum_sr = 0;
594 u16 checksum_local = 0; 610 u16 checksum_local = 0;
595 611
612 /* We must acquire the NVM lock in order to correctly synchronize the
613 * NVM accesses across multiple PFs. Without doing so it is possible
614 * for one of the PFs to read invalid data potentially indicating that
615 * the checksum is invalid.
616 */
617 ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
618 if (ret_code)
619 return ret_code;
596 ret_code = i40e_calc_nvm_checksum(hw, &checksum_local); 620 ret_code = i40e_calc_nvm_checksum(hw, &checksum_local);
621 __i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr);
622 i40e_release_nvm(hw);
597 if (ret_code) 623 if (ret_code)
598 goto i40e_validate_nvm_checksum_exit; 624 return ret_code;
599
600 /* Do not use i40e_read_nvm_word() because we do not want to take
601 * the synchronization semaphores twice here.
602 */
603 i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr);
604 625
605 /* Verify read checksum from EEPROM is the same as 626 /* Verify read checksum from EEPROM is the same as
606 * calculated checksum 627 * calculated checksum
@@ -612,7 +633,6 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw,
612 if (checksum) 633 if (checksum)
613 *checksum = checksum_local; 634 *checksum = checksum_local;
614 635
615i40e_validate_nvm_checksum_exit:
616 return ret_code; 636 return ret_code;
617} 637}
618 638
@@ -997,6 +1017,7 @@ retry:
997 break; 1017 break;
998 1018
999 case I40E_NVMUPD_CSUM_CON: 1019 case I40E_NVMUPD_CSUM_CON:
1020 /* Assumes the caller has acquired the nvm */
1000 status = i40e_update_nvm_checksum(hw); 1021 status = i40e_update_nvm_checksum(hw);
1001 if (status) { 1022 if (status) {
1002 *perrno = hw->aq.asq_last_status ? 1023 *perrno = hw->aq.asq_last_status ?
@@ -1011,6 +1032,7 @@ retry:
1011 break; 1032 break;
1012 1033
1013 case I40E_NVMUPD_CSUM_LCB: 1034 case I40E_NVMUPD_CSUM_LCB:
1035 /* Assumes the caller has acquired the nvm */
1014 status = i40e_update_nvm_checksum(hw); 1036 status = i40e_update_nvm_checksum(hw);
1015 if (status) { 1037 if (status) {
1016 *perrno = hw->aq.asq_last_status ? 1038 *perrno = hw->aq.asq_last_status ?
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index df613ea40313..a39b13197891 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -311,8 +311,6 @@ i40e_status i40e_acquire_nvm(struct i40e_hw *hw,
311void i40e_release_nvm(struct i40e_hw *hw); 311void i40e_release_nvm(struct i40e_hw *hw);
312i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, 312i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
313 u16 *data); 313 u16 *data);
314i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
315 u16 *words, u16 *data);
316i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw); 314i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw);
317i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, 315i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw,
318 u16 *checksum); 316 u16 *checksum);