mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 10:31:33 -04:00
ice: acquire NVM lock around each flash read
FW caps the NVM read lock at a maximum of 3000ms regardless of the timeout
requested via ice_acquire_nvm(). ice_read_flat_nvm() splits a read into
multiple ice_aq_read_nvm() commands, one per 4KB sector, all issued under a
single lock taken by the caller. Reading a large region can exceed 3000ms,
so FW reclaims the lock mid-read and the remaining commands might fail.
Move the lock acquire/release into ice_read_flat_nvm() so it brackets each
individual ice_aq_read_nvm() command, ensuring the lock is never held
across more than one FW read.
ice_release_nvm() issues its own AQ command and overwrites
hw->adminq.sq_last_status, which some callers inspect after a failed read.
Add an optional read_aq_err output parameter to ice_read_flat_nvm() to
capture the failing read's AQ error before the release; callers that need
it (ice_discover_flash_size() and the ethtool/devlink log paths) use it
instead of sq_last_status, others pass NULL.
Callers that previously took the lock around ice_read_flat_nvm(),
ice_read_sr_word() or ice_read_flash_module() now call them without it.
The now-redundant per-block locking in ice_devlink_nvm_snapshot() is
dropped. ice_read_sr_word() is now a thin wrapper, so ice_read_sr_word_aq()
is folded into it.
Fixes: e94509906d ("ice: create function to read a section of the NVM and Shadow RAM")
Signed-off-by: Robert Malz <robert.malz@canonical.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Reviewed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Tested-by: Rinitha S <sx.rinitha@intel.com> (A Contingent worker at Intel)
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
This commit is contained in:
@@ -1890,27 +1890,18 @@ static int ice_devlink_nvm_snapshot(struct devlink *devlink,
|
||||
*/
|
||||
for (i = 0; i < num_blks; i++) {
|
||||
u32 read_sz = min_t(u32, ICE_DEVLINK_READ_BLK_SIZE, left);
|
||||
|
||||
status = ice_acquire_nvm(hw, ICE_RES_READ);
|
||||
if (status) {
|
||||
dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
|
||||
status, hw->adminq.sq_last_status);
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
|
||||
vfree(nvm_data);
|
||||
return -EIO;
|
||||
}
|
||||
enum libie_aq_err read_aq_err = LIBIE_AQ_RC_OK;
|
||||
|
||||
status = ice_read_flat_nvm(hw, i * ICE_DEVLINK_READ_BLK_SIZE,
|
||||
&read_sz, tmp, read_shadow_ram);
|
||||
&read_sz, tmp, read_shadow_ram,
|
||||
&read_aq_err);
|
||||
if (status) {
|
||||
dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n",
|
||||
read_sz, status, hw->adminq.sq_last_status);
|
||||
read_sz, status, read_aq_err);
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to read NVM contents");
|
||||
ice_release_nvm(hw);
|
||||
vfree(nvm_data);
|
||||
return -EIO;
|
||||
}
|
||||
ice_release_nvm(hw);
|
||||
|
||||
tmp += read_sz;
|
||||
left -= read_sz;
|
||||
@@ -1943,6 +1934,7 @@ static int ice_devlink_nvm_read(struct devlink *devlink,
|
||||
struct netlink_ext_ack *extack,
|
||||
u64 offset, u32 size, u8 *data)
|
||||
{
|
||||
enum libie_aq_err read_aq_err = LIBIE_AQ_RC_OK;
|
||||
struct ice_pf *pf = devlink_priv(devlink);
|
||||
struct device *dev = ice_pf_to_dev(pf);
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
@@ -1966,24 +1958,14 @@ static int ice_devlink_nvm_read(struct devlink *devlink,
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
status = ice_acquire_nvm(hw, ICE_RES_READ);
|
||||
if (status) {
|
||||
dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
|
||||
status, hw->adminq.sq_last_status);
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = ice_read_flat_nvm(hw, (u32)offset, &size, data,
|
||||
read_shadow_ram);
|
||||
read_shadow_ram, &read_aq_err);
|
||||
if (status) {
|
||||
dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n",
|
||||
size, status, hw->adminq.sq_last_status);
|
||||
size, status, read_aq_err);
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to read NVM contents");
|
||||
ice_release_nvm(hw);
|
||||
return -EIO;
|
||||
}
|
||||
ice_release_nvm(hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -853,6 +853,7 @@ static int
|
||||
ice_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
|
||||
u8 *bytes)
|
||||
{
|
||||
enum libie_aq_err read_aq_err = LIBIE_AQ_RC_OK;
|
||||
struct ice_pf *pf = ice_netdev_to_pf(netdev);
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
struct device *dev;
|
||||
@@ -869,24 +870,15 @@ ice_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ice_acquire_nvm(hw, ICE_RES_READ);
|
||||
ret = ice_read_flat_nvm(hw, eeprom->offset, &eeprom->len, buf,
|
||||
false, &read_aq_err);
|
||||
if (ret) {
|
||||
dev_err(dev, "ice_acquire_nvm failed, err %d aq_err %s\n",
|
||||
ret, libie_aq_str(hw->adminq.sq_last_status));
|
||||
dev_err(dev, "ice_read_flat_nvm failed, err %d aq_err %s\n",
|
||||
ret, libie_aq_str(read_aq_err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ice_read_flat_nvm(hw, eeprom->offset, &eeprom->len, buf,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dev, "ice_read_flat_nvm failed, err %d aq_err %s\n",
|
||||
ret, libie_aq_str(hw->adminq.sq_last_status));
|
||||
goto release;
|
||||
}
|
||||
|
||||
memcpy(bytes, buf, eeprom->len);
|
||||
release:
|
||||
ice_release_nvm(hw);
|
||||
out:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
|
||||
@@ -53,17 +53,27 @@ int ice_aq_read_nvm(struct ice_hw *hw, u16 module_typeid, u32 offset,
|
||||
* @length: (in) number of bytes to read; (out) number of bytes actually read
|
||||
* @data: buffer to return data in (sized to fit the specified length)
|
||||
* @read_shadow_ram: if true, read from shadow RAM instead of NVM
|
||||
* @read_aq_err: if non-NULL, receives the AQ error status of the failing read
|
||||
*
|
||||
* Reads a portion of the NVM, as a flat memory space. This function correctly
|
||||
* breaks read requests across Shadow RAM sectors and ensures that no single
|
||||
* read request exceeds the maximum 4KB read for a single AdminQ command.
|
||||
*
|
||||
* FW caps the read lock at a maximum of 3000ms, so a read spanning multiple
|
||||
* 4KB sectors cannot be done under a single lock without FW reclaiming it
|
||||
* mid-read. The NVM lock is therefore acquired and released around each AQ
|
||||
* read, so this function must be called without the lock held.
|
||||
*
|
||||
* Since ice_release_nvm() issues an AQ command that overwrites
|
||||
* hw->adminq.sq_last_status, callers that need the failing read's AQ error
|
||||
* must use @read_aq_err rather than inspecting sq_last_status afterwards.
|
||||
*
|
||||
* Returns a status code on failure. Note that the data pointer may be
|
||||
* partially updated if some reads succeed before a failure.
|
||||
*/
|
||||
int
|
||||
ice_read_flat_nvm(struct ice_hw *hw, u32 offset, u32 *length, u8 *data,
|
||||
bool read_shadow_ram)
|
||||
bool read_shadow_ram, enum libie_aq_err *read_aq_err)
|
||||
{
|
||||
u32 inlen = *length;
|
||||
u32 bytes_read = 0;
|
||||
@@ -92,12 +102,30 @@ ice_read_flat_nvm(struct ice_hw *hw, u32 offset, u32 *length, u8 *data,
|
||||
|
||||
last_cmd = !(bytes_read + read_size < inlen);
|
||||
|
||||
status = ice_acquire_nvm(hw, ICE_RES_READ);
|
||||
if (status) {
|
||||
ice_debug(hw, ICE_DBG_NVM, "Failed to acquire NVM lock, err %d aq_err %s\n",
|
||||
status, libie_aq_str(hw->adminq.sq_last_status));
|
||||
break;
|
||||
}
|
||||
|
||||
status = ice_aq_read_nvm(hw, ICE_AQC_NVM_START_POINT,
|
||||
offset, read_size,
|
||||
data + bytes_read, last_cmd,
|
||||
read_shadow_ram, NULL);
|
||||
if (status)
|
||||
if (status) {
|
||||
/* Capture the read's AQ error before ice_release_nvm()
|
||||
* issues its own AQ command and overwrites
|
||||
* sq_last_status.
|
||||
*/
|
||||
if (read_aq_err)
|
||||
*read_aq_err = hw->adminq.sq_last_status;
|
||||
|
||||
ice_release_nvm(hw);
|
||||
break;
|
||||
}
|
||||
|
||||
ice_release_nvm(hw);
|
||||
|
||||
bytes_read += read_size;
|
||||
offset += read_size;
|
||||
@@ -177,14 +205,19 @@ int ice_aq_erase_nvm(struct ice_hw *hw, u16 module_typeid, struct ice_sq_cd *cd)
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_read_sr_word_aq - Reads Shadow RAM via AQ
|
||||
* ice_read_sr_word - Reads Shadow RAM word
|
||||
* @hw: pointer to the HW structure
|
||||
* @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
|
||||
* @data: word read from the Shadow RAM
|
||||
*
|
||||
* Reads one 16 bit word from the Shadow RAM using ice_read_flat_nvm.
|
||||
*
|
||||
* The NVM lock is acquired and released internally by ice_read_flat_nvm()
|
||||
* around the FW read, so this function must be called without the lock held.
|
||||
*
|
||||
* Return: zero on success, or a negative error code on failure.
|
||||
*/
|
||||
static int ice_read_sr_word_aq(struct ice_hw *hw, u16 offset, u16 *data)
|
||||
int ice_read_sr_word(struct ice_hw *hw, u16 offset, u16 *data)
|
||||
{
|
||||
u32 bytes = sizeof(u16);
|
||||
__le16 data_local;
|
||||
@@ -194,7 +227,7 @@ static int ice_read_sr_word_aq(struct ice_hw *hw, u16 offset, u16 *data)
|
||||
* Shadow RAM sector restrictions necessary when reading from the NVM.
|
||||
*/
|
||||
status = ice_read_flat_nvm(hw, offset * sizeof(u16), &bytes,
|
||||
(__force u8 *)&data_local, true);
|
||||
(__force u8 *)&data_local, true, NULL);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
@@ -330,13 +363,8 @@ ice_read_flash_module(struct ice_hw *hw, enum ice_bank_select bank, u16 module,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = ice_acquire_nvm(hw, ICE_RES_READ);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = ice_read_flat_nvm(hw, start + offset, &length, data, false);
|
||||
|
||||
ice_release_nvm(hw);
|
||||
status = ice_read_flat_nvm(hw, start + offset, &length, data, false,
|
||||
NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -418,27 +446,6 @@ ice_read_netlist_module(struct ice_hw *hw, enum ice_bank_select bank, u32 offset
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_read_sr_word - Reads Shadow RAM word and acquire NVM if necessary
|
||||
* @hw: pointer to the HW structure
|
||||
* @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
|
||||
* @data: word read from the Shadow RAM
|
||||
*
|
||||
* Reads one 16 bit word from the Shadow RAM using the ice_read_sr_word_aq.
|
||||
*/
|
||||
int ice_read_sr_word(struct ice_hw *hw, u16 offset, u16 *data)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = ice_acquire_nvm(hw, ICE_RES_READ);
|
||||
if (!status) {
|
||||
status = ice_read_sr_word_aq(hw, offset, data);
|
||||
ice_release_nvm(hw);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_pfa_module_tlv - Reads sub module TLV from NVM PFA
|
||||
* @hw: pointer to hardware structure
|
||||
@@ -856,20 +863,18 @@ int ice_get_inactive_netlist_ver(struct ice_hw *hw, struct ice_netlist_info *net
|
||||
static int ice_discover_flash_size(struct ice_hw *hw)
|
||||
{
|
||||
u32 min_size = 0, max_size = ICE_AQC_NVM_MAX_OFFSET + 1;
|
||||
int status;
|
||||
|
||||
status = ice_acquire_nvm(hw, ICE_RES_READ);
|
||||
if (status)
|
||||
return status;
|
||||
int status = 0;
|
||||
|
||||
while ((max_size - min_size) > 1) {
|
||||
enum libie_aq_err read_aq_err = LIBIE_AQ_RC_OK;
|
||||
u32 offset = (max_size + min_size) / 2;
|
||||
u32 len = 1;
|
||||
u8 data;
|
||||
|
||||
status = ice_read_flat_nvm(hw, offset, &len, &data, false);
|
||||
status = ice_read_flat_nvm(hw, offset, &len, &data, false,
|
||||
&read_aq_err);
|
||||
if (status == -EIO &&
|
||||
hw->adminq.sq_last_status == LIBIE_AQ_RC_EINVAL) {
|
||||
read_aq_err == LIBIE_AQ_RC_EINVAL) {
|
||||
ice_debug(hw, ICE_DBG_NVM, "%s: New upper bound of %u bytes\n",
|
||||
__func__, offset);
|
||||
status = 0;
|
||||
@@ -880,7 +885,7 @@ static int ice_discover_flash_size(struct ice_hw *hw)
|
||||
min_size = offset;
|
||||
} else {
|
||||
/* an unexpected error occurred */
|
||||
goto err_read_flat_nvm;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -888,9 +893,6 @@ static int ice_discover_flash_size(struct ice_hw *hw)
|
||||
|
||||
hw->flash.flash_size = max_size;
|
||||
|
||||
err_read_flat_nvm:
|
||||
ice_release_nvm(hw);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ int ice_aq_read_nvm(struct ice_hw *hw, u16 module_typeid, u32 offset,
|
||||
bool read_shadow_ram, struct ice_sq_cd *cd);
|
||||
int
|
||||
ice_read_flat_nvm(struct ice_hw *hw, u32 offset, u32 *length, u8 *data,
|
||||
bool read_shadow_ram);
|
||||
bool read_shadow_ram, enum libie_aq_err *read_aq_err);
|
||||
int
|
||||
ice_get_pfa_module_tlv(struct ice_hw *hw, u16 *module_tlv, u16 *module_tlv_len,
|
||||
u16 module_type);
|
||||
|
||||
Reference in New Issue
Block a user