diff options
author | erj <erj@FreeBSD.org> | 2016-05-12 18:19:53 +0000 |
---|---|---|
committer | erj <erj@FreeBSD.org> | 2016-05-12 18:19:53 +0000 |
commit | f8cb5159805de7e2978640d2845fede9fe86a703 (patch) | |
tree | 8a98f5999debeacf5635d758197b1e8fd3c31ecb /sys/dev/ixl | |
parent | cf3d6e7a2295f9745c272eeb63c23026ba7a417d (diff) | |
download | FreeBSD-src-f8cb5159805de7e2978640d2845fede9fe86a703.zip FreeBSD-src-f8cb5159805de7e2978640d2845fede9fe86a703.tar.gz |
ixl: Update to 1.4.7-k.
Changes by author:
Helin Zhang i40e_shared: Fix compilation error - pointer-arith
Paul M Stillwell Jr i40e-shared: Replace sprintf with i40e_debug
Anjali Singhai Jain i40e-shared: Fix an accidental error with BIT_ULL replacement
Jesse Brandeburg i40e-shared: remove useless assignments
Anjali Singhai Jain i40e-shared: Add a workaround to drop all flow control frames
Anjali Singhai Jain i40e-shared: Add new response struct from FW for AQ command i40e_aqc_lldp_set_local_mib
Anjali Singhai Jain i40e-shared: Acquire NVM, before issuing an AQ read nvm command
Eric Joyner ixl/ixlv: Remove unused MAX_LOOP define.
Eric Joyner ixl: Remove extra aq_get_link_info() call in attach().
Eric Joyner ixl: Modify a couple error messages in attach() to be more informative.
Eric Joyner ixl: Add i40e_get_link_status() call to init_locked().
Eric Joyner ixl: Move callout_stop() to earlier in ixl_stop().
Eric Joyner ixl: Add extra comments around link ITR code.
Eric Joyner ixl: Attempt to enhance link event handling.
Eric Joyner ixl: Style, spacing, and comment changes.
Eric Joyner ixl: Add I40E_NVM_ACCESS definition.
Eric Joyner ixl: Add interface for nvmupdate tool ioctl to driver.
Eric Joyner ixl: Don't strip out nvm update support from the driver anymore.
Eric Joyner ixl: Interrupts are now allocated/setup and torn down/released on init()/stop().
Differential Revision: https://reviews.freebsd.org/D6211
Reviewed by: sbruno, kmacy, jeffrey.e.pieper@intel.com
MFC after: 2 weeks
Sponsored by: Intel Corporation
Diffstat (limited to 'sys/dev/ixl')
-rw-r--r-- | sys/dev/ixl/i40e_adminq.h | 43 | ||||
-rw-r--r-- | sys/dev/ixl/i40e_adminq_cmd.h | 10 | ||||
-rw-r--r-- | sys/dev/ixl/i40e_common.c | 26 | ||||
-rw-r--r-- | sys/dev/ixl/i40e_nvm.c | 784 | ||||
-rw-r--r-- | sys/dev/ixl/i40e_prototype.h | 2 | ||||
-rw-r--r-- | sys/dev/ixl/i40e_type.h | 3 | ||||
-rw-r--r-- | sys/dev/ixl/if_ixl.c | 258 | ||||
-rw-r--r-- | sys/dev/ixl/ixl.h | 6 | ||||
-rw-r--r-- | sys/dev/ixl/ixl_pf.h | 7 |
9 files changed, 1039 insertions, 100 deletions
diff --git a/sys/dev/ixl/i40e_adminq.h b/sys/dev/ixl/i40e_adminq.h index 76dab8e..ca0e1e9 100644 --- a/sys/dev/ixl/i40e_adminq.h +++ b/sys/dev/ixl/i40e_adminq.h @@ -115,6 +115,49 @@ struct i40e_adminq_info { enum i40e_admin_queue_err arq_last_status; }; +/** + * i40e_aq_rc_to_posix - convert errors to user-land codes + * aq_ret: AdminQ handler error code can override aq_rc + * aq_rc: AdminQ firmware error code to convert + **/ +static INLINE int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) +{ + int aq_to_posix[] = { + 0, /* I40E_AQ_RC_OK */ + -EPERM, /* I40E_AQ_RC_EPERM */ + -ENOENT, /* I40E_AQ_RC_ENOENT */ + -ESRCH, /* I40E_AQ_RC_ESRCH */ + -EINTR, /* I40E_AQ_RC_EINTR */ + -EIO, /* I40E_AQ_RC_EIO */ + -ENXIO, /* I40E_AQ_RC_ENXIO */ + -E2BIG, /* I40E_AQ_RC_E2BIG */ + -EAGAIN, /* I40E_AQ_RC_EAGAIN */ + -ENOMEM, /* I40E_AQ_RC_ENOMEM */ + -EACCES, /* I40E_AQ_RC_EACCES */ + -EFAULT, /* I40E_AQ_RC_EFAULT */ + -EBUSY, /* I40E_AQ_RC_EBUSY */ + -EEXIST, /* I40E_AQ_RC_EEXIST */ + -EINVAL, /* I40E_AQ_RC_EINVAL */ + -ENOTTY, /* I40E_AQ_RC_ENOTTY */ + -ENOSPC, /* I40E_AQ_RC_ENOSPC */ + -ENOSYS, /* I40E_AQ_RC_ENOSYS */ + -ERANGE, /* I40E_AQ_RC_ERANGE */ + -EPIPE, /* I40E_AQ_RC_EFLUSHED */ + -ESPIPE, /* I40E_AQ_RC_BAD_ADDR */ + -EROFS, /* I40E_AQ_RC_EMODE */ + -EFBIG, /* I40E_AQ_RC_EFBIG */ + }; + + /* aq_rc is invalid if AQ timed out */ + if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT) + return -EAGAIN; + + if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) + return -ERANGE; + + return aq_to_posix[aq_rc]; +} + /* general information */ #define I40E_AQ_LARGE_BUF 512 #define I40E_ASQ_CMD_TIMEOUT 250 /* msecs */ diff --git a/sys/dev/ixl/i40e_adminq_cmd.h b/sys/dev/ixl/i40e_adminq_cmd.h index 3cd8cfe..436a82b 100644 --- a/sys/dev/ixl/i40e_adminq_cmd.h +++ b/sys/dev/ixl/i40e_adminq_cmd.h @@ -2158,6 +2158,14 @@ struct i40e_aqc_lldp_set_local_mib { I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_set_local_mib); +struct i40e_aqc_lldp_set_local_mib_resp { +#define SET_LOCAL_MIB_RESP_EVENT_TRIGGERED_MASK 0x01 + u8 status; + u8 reserved[15]; +}; + +I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_lldp_set_local_mib_resp); + /* Stop/Start LLDP Agent (direct 0x0A09) * Used for stopping/starting specific LLDP agent. e.g. DCBx */ @@ -2371,4 +2379,4 @@ struct i40e_aqc_debug_modify_internals { I40E_CHECK_CMD_LENGTH(i40e_aqc_debug_modify_internals); -#endif +#endif /* _I40E_ADMINQ_CMD_H_ */ diff --git a/sys/dev/ixl/i40e_common.c b/sys/dev/ixl/i40e_common.c index 8dc005f..2061294 100644 --- a/sys/dev/ixl/i40e_common.c +++ b/sys/dev/ixl/i40e_common.c @@ -4906,6 +4906,28 @@ enum i40e_status_code i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw, } /** + * i40e_add_filter_to_drop_tx_flow_control_frames- filter to drop flow control + * @hw: pointer to the hw struct + * @seid: VSI seid to add ethertype filter from + **/ +#define I40E_FLOW_CONTROL_ETHTYPE 0x8808 +void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw, + u16 seid) +{ + u16 flag = I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC | + I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP | + I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TX; + u16 ethtype = I40E_FLOW_CONTROL_ETHTYPE; + enum i40e_status_code status; + + status = i40e_aq_add_rem_control_packet_filter(hw, NULL, ethtype, flag, + seid, 0, TRUE, NULL, + NULL); + if (status) + DEBUGOUT("Ethtype Filter Add failed: Error pruning Tx flow control frames\n"); +} + +/** * i40e_aq_add_cloud_filters * @hw: pointer to the hardware structure * @seid: VSI seid to add cloud filters from @@ -5044,8 +5066,6 @@ enum i40e_status_code i40e_aq_alternate_write_indirect(struct i40e_hw *hw, cmd_resp->address = CPU_TO_LE32(addr); cmd_resp->length = CPU_TO_LE32(dw_count); - cmd_resp->addr_high = CPU_TO_LE32(I40E_HI_DWORD((u64)buffer)); - cmd_resp->addr_low = CPU_TO_LE32(I40E_LO_DWORD((u64)buffer)); status = i40e_asq_send_command(hw, &desc, buffer, I40E_LO_DWORD(4*dw_count), NULL); @@ -5127,8 +5147,6 @@ enum i40e_status_code i40e_aq_alternate_read_indirect(struct i40e_hw *hw, cmd_resp->address = CPU_TO_LE32(addr); cmd_resp->length = CPU_TO_LE32(dw_count); - cmd_resp->addr_high = CPU_TO_LE32(I40E_HI_DWORD((u64)buffer)); - cmd_resp->addr_low = CPU_TO_LE32(I40E_LO_DWORD((u64)buffer)); status = i40e_asq_send_command(hw, &desc, buffer, I40E_LO_DWORD(4*dw_count), NULL); diff --git a/sys/dev/ixl/i40e_nvm.c b/sys/dev/ixl/i40e_nvm.c index 623d96a..bd8a45a 100644 --- a/sys/dev/ixl/i40e_nvm.c +++ b/sys/dev/ixl/i40e_nvm.c @@ -218,7 +218,10 @@ static enum i40e_status_code i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw) enum i40e_status_code i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, u16 *data) { - return i40e_read_nvm_word_srctl(hw, offset, data); + enum i40e_status_code ret_code = I40E_SUCCESS; + + ret_code = i40e_read_nvm_word_srctl(hw, offset, data); + return ret_code; } /** @@ -306,7 +309,10 @@ enum i40e_status_code i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, enum i40e_status_code i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, u16 *words, u16 *data) { - return i40e_read_nvm_buffer_srctl(hw, offset, words, data); + enum i40e_status_code ret_code = I40E_SUCCESS; + + ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); + return ret_code; } /** @@ -702,3 +708,777 @@ enum i40e_status_code i40e_validate_nvm_checksum(struct i40e_hw *hw, i40e_validate_nvm_checksum_exit: return ret_code; } + +static enum i40e_status_code i40e_nvmupd_state_init(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static enum i40e_status_code i40e_nvmupd_state_reading(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static enum i40e_status_code i40e_nvmupd_state_writing(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + int *perrno); +static enum i40e_status_code i40e_nvmupd_nvm_erase(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + int *perrno); +static enum i40e_status_code i40e_nvmupd_nvm_write(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static enum i40e_status_code i40e_nvmupd_nvm_read(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static enum i40e_status_code i40e_nvmupd_exec_aq(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static enum i40e_status_code i40e_nvmupd_get_aq_result(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static INLINE u8 i40e_nvmupd_get_module(u32 val) +{ + return (u8)(val & I40E_NVM_MOD_PNT_MASK); +} +static INLINE u8 i40e_nvmupd_get_transaction(u32 val) +{ + return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT); +} + +static const char *i40e_nvm_update_state_str[] = { + "I40E_NVMUPD_INVALID", + "I40E_NVMUPD_READ_CON", + "I40E_NVMUPD_READ_SNT", + "I40E_NVMUPD_READ_LCB", + "I40E_NVMUPD_READ_SA", + "I40E_NVMUPD_WRITE_ERA", + "I40E_NVMUPD_WRITE_CON", + "I40E_NVMUPD_WRITE_SNT", + "I40E_NVMUPD_WRITE_LCB", + "I40E_NVMUPD_WRITE_SA", + "I40E_NVMUPD_CSUM_CON", + "I40E_NVMUPD_CSUM_SA", + "I40E_NVMUPD_CSUM_LCB", + "I40E_NVMUPD_STATUS", + "I40E_NVMUPD_EXEC_AQ", + "I40E_NVMUPD_GET_AQ_RESULT", +}; + +/** + * i40e_nvmupd_command - Process an NVM update command + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * Dispatches command depending on what update state is current + **/ +enum i40e_status_code i40e_nvmupd_command(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + enum i40e_status_code status; + enum i40e_nvmupd_cmd upd_cmd; + + DEBUGFUNC("i40e_nvmupd_command"); + + /* assume success */ + *perrno = 0; + + /* early check for status command and debug msgs */ + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); + + i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n", + i40e_nvm_update_state_str[upd_cmd], + hw->nvmupd_state, + hw->aq.nvm_release_on_done); + + if (upd_cmd == I40E_NVMUPD_INVALID) { + *perrno = -EFAULT; + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_validate_command returns %d errno %d\n", + upd_cmd, *perrno); + } + + /* a status request returns immediately rather than + * going into the state machine + */ + if (upd_cmd == I40E_NVMUPD_STATUS) { + bytes[0] = hw->nvmupd_state; + return I40E_SUCCESS; + } + + switch (hw->nvmupd_state) { + case I40E_NVMUPD_STATE_INIT: + status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_STATE_READING: + status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_STATE_WRITING: + status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_STATE_INIT_WAIT: + case I40E_NVMUPD_STATE_WRITE_WAIT: + status = I40E_ERR_NOT_READY; + *perrno = -EBUSY; + break; + + default: + /* invalid state, should never happen */ + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: no such state %d\n", hw->nvmupd_state); + status = I40E_NOT_SUPPORTED; + *perrno = -ESRCH; + break; + } + return status; +} + +/** + * i40e_nvmupd_state_init - Handle NVM update state Init + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * Process legitimate commands of the Init state and conditionally set next + * state. Reject all other commands. + **/ +static enum i40e_status_code i40e_nvmupd_state_init(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + enum i40e_status_code status = I40E_SUCCESS; + enum i40e_nvmupd_cmd upd_cmd; + + DEBUGFUNC("i40e_nvmupd_state_init"); + + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); + + switch (upd_cmd) { + case I40E_NVMUPD_READ_SA: + status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (status) { + *perrno = i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status); + } else { + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); + i40e_release_nvm(hw); + } + break; + + case I40E_NVMUPD_READ_SNT: + status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (status) { + *perrno = i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status); + } else { + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); + if (status) + i40e_release_nvm(hw); + else + hw->nvmupd_state = I40E_NVMUPD_STATE_READING; + } + break; + + case I40E_NVMUPD_WRITE_ERA: + status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); + if (status) { + *perrno = i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status); + } else { + status = i40e_nvmupd_nvm_erase(hw, cmd, perrno); + if (status) { + i40e_release_nvm(hw); + } else { + hw->aq.nvm_release_on_done = TRUE; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } + } + break; + + case I40E_NVMUPD_WRITE_SA: + status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); + if (status) { + *perrno = i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status); + } else { + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) { + i40e_release_nvm(hw); + } else { + hw->aq.nvm_release_on_done = TRUE; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } + } + break; + + case I40E_NVMUPD_WRITE_SNT: + status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); + if (status) { + *perrno = i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status); + } else { + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) + i40e_release_nvm(hw); + else + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; + } + break; + + case I40E_NVMUPD_CSUM_SA: + status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); + if (status) { + *perrno = i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status); + } else { + status = i40e_update_nvm_checksum(hw); + if (status) { + *perrno = hw->aq.asq_last_status ? + i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status) : + -EIO; + i40e_release_nvm(hw); + } else { + hw->aq.nvm_release_on_done = TRUE; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } + } + break; + + case I40E_NVMUPD_EXEC_AQ: + status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_GET_AQ_RESULT: + status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno); + break; + + default: + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: bad cmd %s in init state\n", + i40e_nvm_update_state_str[upd_cmd]); + status = I40E_ERR_NVM; + *perrno = -ESRCH; + break; + } + return status; +} + +/** + * i40e_nvmupd_state_reading - Handle NVM update state Reading + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * NVM ownership is already held. Process legitimate commands and set any + * change in state; reject all other commands. + **/ +static enum i40e_status_code i40e_nvmupd_state_reading(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + enum i40e_status_code status = I40E_SUCCESS; + enum i40e_nvmupd_cmd upd_cmd; + + DEBUGFUNC("i40e_nvmupd_state_reading"); + + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); + + switch (upd_cmd) { + case I40E_NVMUPD_READ_SA: + case I40E_NVMUPD_READ_CON: + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_READ_LCB: + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); + i40e_release_nvm(hw); + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + break; + + default: + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: bad cmd %s in reading state.\n", + i40e_nvm_update_state_str[upd_cmd]); + status = I40E_NOT_SUPPORTED; + *perrno = -ESRCH; + break; + } + return status; +} + +/** + * i40e_nvmupd_state_writing - Handle NVM update state Writing + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * NVM ownership is already held. Process legitimate commands and set any + * change in state; reject all other commands + **/ +static enum i40e_status_code i40e_nvmupd_state_writing(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + enum i40e_status_code status = I40E_SUCCESS; + enum i40e_nvmupd_cmd upd_cmd; + bool retry_attempt = FALSE; + + DEBUGFUNC("i40e_nvmupd_state_writing"); + + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); + +retry: + switch (upd_cmd) { + case I40E_NVMUPD_WRITE_CON: + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (!status) + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; + break; + + case I40E_NVMUPD_WRITE_LCB: + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) { + *perrno = hw->aq.asq_last_status ? + i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status) : + -EIO; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { + hw->aq.nvm_release_on_done = TRUE; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } + break; + + case I40E_NVMUPD_CSUM_CON: + status = i40e_update_nvm_checksum(hw); + if (status) { + *perrno = hw->aq.asq_last_status ? + i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status) : + -EIO; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; + } + break; + + case I40E_NVMUPD_CSUM_LCB: + status = i40e_update_nvm_checksum(hw); + if (status) { + *perrno = hw->aq.asq_last_status ? + i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status) : + -EIO; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { + hw->aq.nvm_release_on_done = TRUE; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } + break; + + default: + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: bad cmd %s in writing state.\n", + i40e_nvm_update_state_str[upd_cmd]); + status = I40E_NOT_SUPPORTED; + *perrno = -ESRCH; + break; + } + + /* In some circumstances, a multi-write transaction takes longer + * than the default 3 minute timeout on the write semaphore. If + * the write failed with an EBUSY status, this is likely the problem, + * so here we try to reacquire the semaphore then retry the write. + * We only do one retry, then give up. + */ + if (status && (hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) && + !retry_attempt) { + enum i40e_status_code old_status = status; + u32 old_asq_status = hw->aq.asq_last_status; + u32 gtime; + + gtime = rd32(hw, I40E_GLVFGEN_TIMER); + if (gtime >= hw->nvm.hw_semaphore_timeout) { + i40e_debug(hw, I40E_DEBUG_ALL, + "NVMUPD: write semaphore expired (%d >= %lld), retrying\n", + gtime, hw->nvm.hw_semaphore_timeout); + i40e_release_nvm(hw); + status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); + if (status) { + i40e_debug(hw, I40E_DEBUG_ALL, + "NVMUPD: write semaphore reacquire failed aq_err = %d\n", + hw->aq.asq_last_status); + status = old_status; + hw->aq.asq_last_status = old_asq_status; + } else { + retry_attempt = TRUE; + goto retry; + } + } + } + + return status; +} + +/** + * i40e_nvmupd_validate_command - Validate given command + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @perrno: pointer to return error code + * + * Return one of the valid command types or I40E_NVMUPD_INVALID + **/ +static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + int *perrno) +{ + enum i40e_nvmupd_cmd upd_cmd; + u8 module, transaction; + + DEBUGFUNC("i40e_nvmupd_validate_command\n"); + + /* anything that doesn't match a recognized case is an error */ + upd_cmd = I40E_NVMUPD_INVALID; + + transaction = i40e_nvmupd_get_transaction(cmd->config); + module = i40e_nvmupd_get_module(cmd->config); + + /* limits on data size */ + if ((cmd->data_size < 1) || + (cmd->data_size > I40E_NVMUPD_MAX_DATA)) { + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_validate_command data_size %d\n", + cmd->data_size); + *perrno = -EFAULT; + return I40E_NVMUPD_INVALID; + } + + switch (cmd->command) { + case I40E_NVM_READ: + switch (transaction) { + case I40E_NVM_CON: + upd_cmd = I40E_NVMUPD_READ_CON; + break; + case I40E_NVM_SNT: + upd_cmd = I40E_NVMUPD_READ_SNT; + break; + case I40E_NVM_LCB: + upd_cmd = I40E_NVMUPD_READ_LCB; + break; + case I40E_NVM_SA: + upd_cmd = I40E_NVMUPD_READ_SA; + break; + case I40E_NVM_EXEC: + if (module == 0xf) + upd_cmd = I40E_NVMUPD_STATUS; + else if (module == 0) + upd_cmd = I40E_NVMUPD_GET_AQ_RESULT; + break; + } + break; + + case I40E_NVM_WRITE: + switch (transaction) { + case I40E_NVM_CON: + upd_cmd = I40E_NVMUPD_WRITE_CON; + break; + case I40E_NVM_SNT: + upd_cmd = I40E_NVMUPD_WRITE_SNT; + break; + case I40E_NVM_LCB: + upd_cmd = I40E_NVMUPD_WRITE_LCB; + break; + case I40E_NVM_SA: + upd_cmd = I40E_NVMUPD_WRITE_SA; + break; + case I40E_NVM_ERA: + upd_cmd = I40E_NVMUPD_WRITE_ERA; + break; + case I40E_NVM_CSUM: + upd_cmd = I40E_NVMUPD_CSUM_CON; + break; + case (I40E_NVM_CSUM|I40E_NVM_SA): + upd_cmd = I40E_NVMUPD_CSUM_SA; + break; + case (I40E_NVM_CSUM|I40E_NVM_LCB): + upd_cmd = I40E_NVMUPD_CSUM_LCB; + break; + case I40E_NVM_EXEC: + if (module == 0) + upd_cmd = I40E_NVMUPD_EXEC_AQ; + break; + } + break; + } + + return upd_cmd; +} + +/** + * i40e_nvmupd_exec_aq - Run an AQ command + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static enum i40e_status_code i40e_nvmupd_exec_aq(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + struct i40e_asq_cmd_details cmd_details; + enum i40e_status_code status; + struct i40e_aq_desc *aq_desc; + u32 buff_size = 0; + u8 *buff = NULL; + u32 aq_desc_len; + u32 aq_data_len; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + + aq_desc_len = sizeof(struct i40e_aq_desc); + memset(&hw->nvm_wb_desc, 0, aq_desc_len); + + /* get the aq descriptor */ + if (cmd->data_size < aq_desc_len) { + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n", + cmd->data_size, aq_desc_len); + *perrno = -EINVAL; + return I40E_ERR_PARAM; + } + aq_desc = (struct i40e_aq_desc *)bytes; + + /* if data buffer needed, make sure it's ready */ + aq_data_len = cmd->data_size - aq_desc_len; + buff_size = max(aq_data_len, (u32)LE16_TO_CPU(aq_desc->datalen)); + if (buff_size) { + if (!hw->nvm_buff.va) { + status = i40e_allocate_virt_mem(hw, &hw->nvm_buff, + hw->aq.asq_buf_size); + if (status) + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n", + status); + } + + if (hw->nvm_buff.va) { + buff = hw->nvm_buff.va; + memcpy(buff, &bytes[aq_desc_len], aq_data_len); + } + } + + /* and away we go! */ + status = i40e_asq_send_command(hw, aq_desc, buff, + buff_size, &cmd_details); + if (status) { + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_exec_aq err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + } + + return status; +} + +/** + * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static enum i40e_status_code i40e_nvmupd_get_aq_result(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + u32 aq_total_len; + u32 aq_desc_len; + int remainder; + u8 *buff; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + + aq_desc_len = sizeof(struct i40e_aq_desc); + aq_total_len = aq_desc_len + LE16_TO_CPU(hw->nvm_wb_desc.datalen); + + /* check offset range */ + if (cmd->offset > aq_total_len) { + i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n", + __func__, cmd->offset, aq_total_len); + *perrno = -EINVAL; + return I40E_ERR_PARAM; + } + + /* check copylength range */ + if (cmd->data_size > (aq_total_len - cmd->offset)) { + int new_len = aq_total_len - cmd->offset; + + i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n", + __func__, cmd->data_size, new_len); + cmd->data_size = new_len; + } + + remainder = cmd->data_size; + if (cmd->offset < aq_desc_len) { + u32 len = aq_desc_len - cmd->offset; + + len = min(len, cmd->data_size); + i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n", + __func__, cmd->offset, cmd->offset + len); + + buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset; + memcpy(bytes, buff, len); + + bytes += len; + remainder -= len; + buff = hw->nvm_buff.va; + } else { + buff = (u8 *)hw->nvm_buff.va + (cmd->offset - aq_desc_len); + } + + if (remainder > 0) { + int start_byte = buff - (u8 *)hw->nvm_buff.va; + + i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n", + __func__, start_byte, start_byte + remainder); + memcpy(bytes, buff, remainder); + } + + return I40E_SUCCESS; +} + +/** + * i40e_nvmupd_nvm_read - Read NVM + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static enum i40e_status_code i40e_nvmupd_nvm_read(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + struct i40e_asq_cmd_details cmd_details; + enum i40e_status_code status; + u8 module, transaction; + bool last; + + transaction = i40e_nvmupd_get_transaction(cmd->config); + module = i40e_nvmupd_get_module(cmd->config); + last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA); + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + + status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size, + bytes, last, &cmd_details); + if (status) { + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n", + module, cmd->offset, cmd->data_size); + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_nvm_read status %d aq %d\n", + status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + } + + return status; +} + +/** + * i40e_nvmupd_nvm_erase - Erase an NVM module + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @perrno: pointer to return error code + * + * module, offset, data_size and data are in cmd structure + **/ +static enum i40e_status_code i40e_nvmupd_nvm_erase(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + int *perrno) +{ + enum i40e_status_code status = I40E_SUCCESS; + struct i40e_asq_cmd_details cmd_details; + u8 module, transaction; + bool last; + + transaction = i40e_nvmupd_get_transaction(cmd->config); + module = i40e_nvmupd_get_module(cmd->config); + last = (transaction & I40E_NVM_LCB); + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + + status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size, + last, &cmd_details); + if (status) { + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n", + module, cmd->offset, cmd->data_size); + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_nvm_erase status %d aq %d\n", + status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + } + + return status; +} + +/** + * i40e_nvmupd_nvm_write - Write NVM + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * module, offset, data_size and data are in cmd structure + **/ +static enum i40e_status_code i40e_nvmupd_nvm_write(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + enum i40e_status_code status = I40E_SUCCESS; + struct i40e_asq_cmd_details cmd_details; + u8 module, transaction; + bool last; + + transaction = i40e_nvmupd_get_transaction(cmd->config); + module = i40e_nvmupd_get_module(cmd->config); + last = (transaction & I40E_NVM_LCB); + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + + status = i40e_aq_update_nvm(hw, module, cmd->offset, + (u16)cmd->data_size, bytes, last, + &cmd_details); + if (status) { + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n", + module, cmd->offset, cmd->data_size); + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_nvm_write status %d aq %d\n", + status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + } + + return status; +} diff --git a/sys/dev/ixl/i40e_prototype.h b/sys/dev/ixl/i40e_prototype.h index 604813c..cfecacb 100644 --- a/sys/dev/ixl/i40e_prototype.h +++ b/sys/dev/ixl/i40e_prototype.h @@ -462,4 +462,6 @@ enum i40e_status_code i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id, void *buff, u16 *ret_buff_size, u8 *ret_next_table, u32 *ret_next_index, struct i40e_asq_cmd_details *cmd_details); +void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw, + u16 vsi_seid); #endif /* _I40E_PROTOTYPE_H_ */ diff --git a/sys/dev/ixl/i40e_type.h b/sys/dev/ixl/i40e_type.h index 357114f..038e01c 100644 --- a/sys/dev/ixl/i40e_type.h +++ b/sys/dev/ixl/i40e_type.h @@ -619,6 +619,9 @@ struct i40e_hw { struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */ struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */ +#define I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE BIT_ULL(0) + u64 flags; + /* debug mask */ u32 debug_mask; char err_str[16]; diff --git a/sys/dev/ixl/if_ixl.c b/sys/dev/ixl/if_ixl.c index 7ae2503..ca4644f 100644 --- a/sys/dev/ixl/if_ixl.c +++ b/sys/dev/ixl/if_ixl.c @@ -48,7 +48,7 @@ /********************************************************************* * Driver version *********************************************************************/ -char ixl_driver_version[] = "1.4.6-k"; +char ixl_driver_version[] = "1.4.7-k"; /********************************************************************* * PCI Device ID Table @@ -99,6 +99,7 @@ static int ixl_ioctl(struct ifnet *, u_long, caddr_t); static void ixl_init(void *); static void ixl_init_locked(struct ixl_pf *); static void ixl_stop(struct ixl_pf *); +static void ixl_stop_locked(struct ixl_pf *); static void ixl_media_status(struct ifnet *, struct ifmediareq *); static int ixl_media_change(struct ifnet *); static void ixl_update_link_status(struct ixl_pf *); @@ -115,6 +116,7 @@ static void ixl_configure_itr(struct ixl_pf *); static void ixl_configure_legacy(struct ixl_pf *); static void ixl_init_taskqueues(struct ixl_pf *); static void ixl_free_taskqueues(struct ixl_pf *); +static void ixl_free_interrupt_resources(struct ixl_pf *); static void ixl_free_pci_resources(struct ixl_pf *); static void ixl_local_timer(void *); static int ixl_setup_interface(device_t, struct ixl_vsi *); @@ -191,6 +193,8 @@ static void ixl_stat_update48(struct i40e_hw *, u32, u32, bool, u64 *, u64 *); static void ixl_stat_update32(struct i40e_hw *, u32, bool, u64 *, u64 *); +/* NVM update */ +static int ixl_handle_nvmupd_cmd(struct ixl_pf *, struct ifdrv *); #ifdef IXL_DEBUG_SYSCTL static int ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS); @@ -200,6 +204,7 @@ static int ixl_sysctl_hw_res_alloc(SYSCTL_HANDLER_ARGS); static int ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS); #endif + #ifdef PCI_IOV static int ixl_adminq_err_to_errno(enum i40e_admin_queue_err err); @@ -634,28 +639,22 @@ ixl_attach(device_t dev) /* Initialize mac filter list for VSI */ SLIST_INIT(&vsi->ftl); - /* Set up interrupt routing here */ - if (pf->msix > 1) - error = ixl_assign_vsi_msix(pf); - else - error = ixl_assign_vsi_legacy(pf); - if (error) - goto err_mac_hmc; - if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) || (hw->aq.fw_maj_ver < 4)) { i40e_msec_delay(75); error = i40e_aq_set_link_restart_an(hw, TRUE, NULL); - if (error) + if (error) { device_printf(dev, "link restart failed, aq_err=%d\n", pf->hw.aq.asq_last_status); + goto err_late; + } } /* Determine link state */ - i40e_aq_get_link_info(hw, TRUE, NULL, NULL); + hw->phy.get_link_info = TRUE; i40e_get_link_status(hw, &pf->link_up); - /* Setup OS specific network interface */ + /* Setup OS network interface / ifnet */ if (ixl_setup_interface(dev, vsi) != 0) { device_printf(dev, "interface setup failed!\n"); error = EIO; @@ -664,15 +663,19 @@ ixl_attach(device_t dev) error = ixl_switch_config(pf); if (error) { - device_printf(dev, "Initial switch config failed: %d\n", error); + device_printf(dev, "Initial ixl_switch_config() failed: %d\n", error); goto err_late; } - /* Limit phy interrupts to link and modules failure */ + /* Limit PHY interrupts to link, autoneg, and modules failure */ error = i40e_aq_set_phy_int_mask(hw, - I40E_AQ_EVENT_LINK_UPDOWN | I40E_AQ_EVENT_MODULE_QUAL_FAIL, NULL); - if (error) - device_printf(dev, "set phy mask failed: %d\n", error); + I40E_AQ_EVENT_LINK_UPDOWN | I40E_AQ_EVENT_MODULE_QUAL_FAIL, + NULL); + if (error) { + device_printf(dev, "i40e_aq_set_phy_mask() failed: err %d," + " aq_err %d\n", error, hw->aq.asq_last_status); + goto err_late; + } /* Get the bus configuration and set the shared code */ bus = ixl_get_bus_info(hw, dev); @@ -771,11 +774,8 @@ ixl_detach(device_t dev) #endif ether_ifdetach(vsi->ifp); - if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) { - IXL_PF_LOCK(pf); + if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) ixl_stop(pf); - IXL_PF_UNLOCK(pf); - } ixl_free_taskqueues(pf); @@ -819,9 +819,7 @@ static int ixl_shutdown(device_t dev) { struct ixl_pf *pf = device_get_softc(dev); - IXL_PF_LOCK(pf); ixl_stop(pf); - IXL_PF_UNLOCK(pf); return (0); } @@ -970,7 +968,8 @@ ixl_ioctl(struct ifnet * ifp, u_long command, caddr_t data) { struct ixl_vsi *vsi = ifp->if_softc; struct ixl_pf *pf = vsi->back; - struct ifreq *ifr = (struct ifreq *) data; + struct ifreq *ifr = (struct ifreq *)data; + struct ifdrv *ifd = (struct ifdrv *)data; #if defined(INET) || defined(INET6) struct ifaddr *ifa = (struct ifaddr *)data; bool avoid_reset = FALSE; @@ -1029,14 +1028,32 @@ ixl_ioctl(struct ifnet * ifp, u_long command, caddr_t data) (IFF_PROMISC | IFF_ALLMULTI)) { ixl_set_promisc(vsi); } - } else - ixl_init_locked(pf); - } else - if (ifp->if_drv_flags & IFF_DRV_RUNNING) + } else { + IXL_PF_UNLOCK(pf); + ixl_init(pf); + IXL_PF_LOCK(pf); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + IXL_PF_UNLOCK(pf); ixl_stop(pf); + IXL_PF_LOCK(pf); + } + } pf->if_flags = ifp->if_flags; IXL_PF_UNLOCK(pf); break; + case SIOCSDRVSPEC: + case SIOCGDRVSPEC: + IOCTL_DEBUGOUT("ioctl: SIOCxDRVSPEC (Get/Set Driver-specific " + "Info)\n"); + + /* NVM update command */ + if (ifd->ifd_cmd == I40E_NVM_ACCESS) + error = ixl_handle_nvmupd_cmd(pf, ifd); + else + error = EINVAL; + break; case SIOCADDMULTI: IOCTL_DEBUGOUT("ioctl: SIOCADDMULTI"); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { @@ -1128,7 +1145,8 @@ ixl_init_locked(struct ixl_pf *pf) mtx_assert(&pf->pf_mtx, MA_OWNED); INIT_DEBUGOUT("ixl_init: begin"); - ixl_stop(pf); + + ixl_stop_locked(pf); /* Get the latest mac address... User might use a LAA */ bcopy(IF_LLADDR(vsi->ifp), tmpaddr, @@ -1213,6 +1231,11 @@ ixl_init_locked(struct ixl_pf *pf) /* And now turn on interrupts */ ixl_enable_intr(vsi); + /* Get link info */ + hw->phy.get_link_info = TRUE; + i40e_get_link_status(hw, &pf->link_up); + ixl_update_link_status(pf); + /* Now inform the stack we're ready */ ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; @@ -1224,6 +1247,17 @@ static void ixl_init(void *arg) { struct ixl_pf *pf = arg; + int ret = 0; + + /* Set up interrupt routing here */ + if (pf->msix > 1) + ret = ixl_assign_vsi_msix(pf); + else + ret = ixl_assign_vsi_legacy(pf); + if (ret) { + device_printf(pf->dev, "assign_vsi_msix/legacy error: %d\n", ret); + return; + } IXL_PF_LOCK(pf); ixl_init_locked(pf); @@ -1861,7 +1895,7 @@ ixl_update_link_status(struct ixl_pf *pf) struct ifnet *ifp = vsi->ifp; device_t dev = pf->dev; - if (pf->link_up){ + if (pf->link_up) { if (vsi->link_active == FALSE) { pf->fc = hw->fc.current_mode; if (bootverbose) { @@ -1888,7 +1922,7 @@ ixl_update_link_status(struct ixl_pf *pf) } else { /* Link down */ if (vsi->link_active == TRUE) { if (bootverbose) - device_printf(dev,"Link is Down\n"); + device_printf(dev, "Link is Down\n"); if_link_state_change(ifp, LINK_STATE_DOWN); vsi->link_active = FALSE; } @@ -1897,6 +1931,16 @@ ixl_update_link_status(struct ixl_pf *pf) return; } +static void +ixl_stop(struct ixl_pf *pf) +{ + IXL_PF_LOCK(pf); + ixl_stop_locked(pf); + IXL_PF_UNLOCK(pf); + + ixl_free_interrupt_resources(pf); +} + /********************************************************************* * * This routine disables all traffic on the adapter by issuing a @@ -1905,14 +1949,18 @@ ixl_update_link_status(struct ixl_pf *pf) **********************************************************************/ static void -ixl_stop(struct ixl_pf *pf) +ixl_stop_locked(struct ixl_pf *pf) { struct ixl_vsi *vsi = &pf->vsi; struct ifnet *ifp = vsi->ifp; - mtx_assert(&pf->pf_mtx, MA_OWNED); - INIT_DEBUGOUT("ixl_stop: begin\n"); + + IXL_PF_LOCK_ASSERT(pf); + + /* Stop the local timer */ + callout_stop(&pf->timer); + if (pf->num_vfs == 0) ixl_disable_intr(vsi); else @@ -1922,9 +1970,6 @@ ixl_stop(struct ixl_pf *pf) /* Tell the stack that the interface is no longer active */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - /* Stop the local timer */ - callout_stop(&pf->timer); - return; } @@ -2228,17 +2273,17 @@ msi: ixl_max_queues = 1; ixl_enable_msix = 0; if (vectors == 1 && pci_alloc_msi(dev, &vectors) == 0) - device_printf(pf->dev,"Using an MSI interrupt\n"); + device_printf(pf->dev, "Using an MSI interrupt\n"); else { pf->msix = 0; - device_printf(pf->dev,"Using a Legacy interrupt\n"); + device_printf(pf->dev, "Using a Legacy interrupt\n"); } return (vectors); } /* - * Plumb MSI/X vectors + * Plumb MSIX vectors */ static void ixl_configure_msix(struct ixl_pf *pf) @@ -2261,8 +2306,14 @@ ixl_configure_msix(struct ixl_pf *pf) I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK; wr32(hw, I40E_PFINT_ICR0_ENA, reg); + /* + * 0x7FF is the end of the queue list. + * This means we won't use MSI-X vector 0 for a queue interrupt + * in MSIX mode. + */ wr32(hw, I40E_PFINT_LNKLST0, 0x7FF); - wr32(hw, I40E_PFINT_ITR0(IXL_RX_ITR), 0x003E); + /* Value is in 2 usec units, so 0x3E is 62*2 = 124 usecs. */ + wr32(hw, I40E_PFINT_ITR0(IXL_RX_ITR), 0x3E); wr32(hw, I40E_PFINT_DYN_CTL0, I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK | @@ -2303,11 +2354,9 @@ ixl_configure_legacy(struct ixl_pf *pf) struct i40e_hw *hw = &pf->hw; u32 reg; - wr32(hw, I40E_PFINT_ITR0(0), 0); wr32(hw, I40E_PFINT_ITR0(1), 0); - /* Setup "other" causes */ reg = I40E_PFINT_ICR0_ENA_ECC_ERR_MASK | I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK @@ -2422,14 +2471,12 @@ ixl_allocate_pci_resources(struct ixl_pf *pf) } static void -ixl_free_pci_resources(struct ixl_pf * pf) +ixl_free_interrupt_resources(struct ixl_pf *pf) { struct ixl_vsi *vsi = &pf->vsi; struct ixl_queue *que = vsi->queues; device_t dev = pf->dev; - int rid, memrid; - - memrid = PCIR_BAR(IXL_BAR); + int rid; /* We may get here before stations are setup */ if ((!ixl_enable_msix) || (que == NULL)) @@ -2444,8 +2491,10 @@ ixl_free_pci_resources(struct ixl_pf * pf) bus_teardown_intr(dev, que->res, que->tag); que->tag = NULL; } - if (que->res != NULL) + if (que->res != NULL) { bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); + que->res = NULL; + } } early: @@ -2459,12 +2508,25 @@ early: bus_teardown_intr(dev, pf->res, pf->tag); pf->tag = NULL; } - if (pf->res != NULL) + if (pf->res != NULL) { bus_release_resource(dev, SYS_RES_IRQ, rid, pf->res); + pf->res = NULL; + } +} + +static void +ixl_free_pci_resources(struct ixl_pf *pf) +{ + device_t dev = pf->dev; + int memrid; + + ixl_free_interrupt_resources(pf); if (pf->msix) pci_release_msi(dev); + memrid = PCIR_BAR(IXL_BAR); + if (pf->msix_mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, memrid, pf->msix_mem); @@ -2665,31 +2727,35 @@ ixl_setup_interface(device_t dev, struct ixl_vsi *vsi) } /* -** Run when the Admin Queue gets a -** link transition interrupt. +** Run when the Admin Queue gets a link state change interrupt. */ static void ixl_link_event(struct ixl_pf *pf, struct i40e_arq_event_info *e) { struct i40e_hw *hw = &pf->hw; + device_t dev = pf->dev; struct i40e_aqc_get_link_status *status = (struct i40e_aqc_get_link_status *)&e->desc.params.raw; - bool check; + /* Firmware workaround: may need to wait for link to actually come up... */ + if (!pf->link_up && (status->link_info & I40E_AQ_SIGNAL_DETECT)) { + device_printf(dev, "%s: Waiting...\n", __func__); + i40e_msec_delay(4000); + } + + /* Request link status from adapter */ hw->phy.get_link_info = TRUE; - i40e_get_link_status(hw, &check); - pf->link_up = check; -#ifdef IXL_DEBUG - printf("Link is %s\n", check ? "up":"down"); -#endif - /* Report if Unqualified modules are found */ + i40e_get_link_status(hw, &pf->link_up); + + /* Print out message if an unqualified module is found */ if ((status->link_info & I40E_AQ_MEDIA_AVAILABLE) && (!(status->an_info & I40E_AQ_QUALIFIED_MODULE)) && (!(status->link_info & I40E_AQ_LINK_UP))) - device_printf(pf->dev, "Link failed because " - "an unqualified module was detected\n"); + device_printf(dev, "Link failed because " + "an unqualified module was detected!\n"); - return; + /* Update OS link info */ + ixl_update_link_status(pf); } /********************************************************************* @@ -3040,7 +3106,7 @@ ixl_setup_stations(struct ixl_pf *pf) } /* Allocate a buf ring */ txr->br = buf_ring_alloc(4096, M_DEVBUF, - M_WAITOK, &txr->mtx); + M_NOWAIT, &txr->mtx); if (txr->br == NULL) { device_printf(dev, "Critical Failure setting up TX buf ring\n"); @@ -4098,7 +4164,6 @@ ixl_enable_adminq(struct i40e_hw *hw) (IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT); wr32(hw, I40E_PFINT_DYN_CTL0, reg); ixl_flush(hw); - return; } static void @@ -4108,8 +4173,7 @@ ixl_disable_adminq(struct i40e_hw *hw) reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT; wr32(hw, I40E_PFINT_DYN_CTL0, reg); - - return; + ixl_flush(hw); } static void @@ -4130,8 +4194,6 @@ ixl_disable_queue(struct i40e_hw *hw, int id) reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT; wr32(hw, I40E_PFINT_DYN_CTLN(id), reg); - - return; } static void @@ -4151,8 +4213,6 @@ ixl_disable_legacy(struct i40e_hw *hw) reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT; wr32(hw, I40E_PFINT_DYN_CTL0, reg); - - return; } static void @@ -4340,17 +4400,18 @@ ixl_do_adminq(void *context, int pending) { struct ixl_pf *pf = context; struct i40e_hw *hw = &pf->hw; - struct ixl_vsi *vsi = &pf->vsi; struct i40e_arq_event_info event; i40e_status ret; - u32 reg, loop = 0; + device_t dev = pf->dev; + u32 loop = 0; u16 opcode, result; event.buf_len = IXL_AQ_BUF_SZ; event.msg_buf = malloc(event.buf_len, M_DEVBUF, M_NOWAIT | M_ZERO); if (!event.msg_buf) { - printf("Unable to allocate adminq memory\n"); + device_printf(dev, "%s: Unable to allocate memory for Admin" + " Queue event!\n", __func__); return; } @@ -4361,10 +4422,12 @@ ixl_do_adminq(void *context, int pending) if (ret) break; opcode = LE16_TO_CPU(event.desc.opcode); +#ifdef IXL_DEBUG + device_printf(dev, "%s: Admin Queue event: %#06x\n", __func__, opcode); +#endif switch (opcode) { case i40e_aqc_opc_get_link_status: ixl_link_event(pf, &event); - ixl_update_link_status(pf); break; case i40e_aqc_opc_send_msg_to_pf: #ifdef PCI_IOV @@ -4372,19 +4435,12 @@ ixl_do_adminq(void *context, int pending) #endif break; case i40e_aqc_opc_event_lan_overflow: - break; default: -#ifdef IXL_DEBUG - printf("AdminQ unknown event %x\n", opcode); -#endif break; } } while (result && (loop++ < IXL_ADM_LIMIT)); - reg = rd32(hw, I40E_PFINT_ICR0_ENA); - reg |= I40E_PFINT_ICR0_ENA_ADMINQ_MASK; - wr32(hw, I40E_PFINT_ICR0_ENA, reg); free(event.msg_buf, M_DEVBUF); /* @@ -4394,7 +4450,7 @@ ixl_do_adminq(void *context, int pending) if (result > 0) taskqueue_enqueue(pf->tq, &pf->adminq); else - ixl_enable_intr(vsi); + ixl_enable_adminq(hw); IXL_PF_UNLOCK(pf); } @@ -4650,11 +4706,9 @@ static int ixl_set_flowcntl(SYSCTL_HANDLER_ARGS) { /* - * TODO: ensure flow control is disabled if - * priority flow control is enabled - * * TODO: ensure tx CRC by hardware should be enabled * if tx flow control is enabled. + * ^ N/A for 40G ports */ struct ixl_pf *pf = (struct ixl_pf *)arg1; struct i40e_hw *hw = &pf->hw; @@ -4691,9 +4745,14 @@ ixl_set_flowcntl(SYSCTL_HANDLER_ARGS) device_printf(dev, "%s: Error setting new fc mode %d; fc_err %#x\n", __func__, aq_error, fc_aq_err); - return (EAGAIN); + return (EIO); } + /* Get new link state */ + i40e_msec_delay(250); + hw->phy.get_link_info = TRUE; + i40e_get_link_status(hw, &pf->link_up); + return (0); } @@ -4795,7 +4854,7 @@ ixl_set_advertised_speeds(struct ixl_pf *pf, int speeds) ** need to get a reinit on some devices */ IXL_PF_LOCK(pf); - ixl_stop(pf); + ixl_stop_locked(pf); ixl_init_locked(pf); IXL_PF_UNLOCK(pf); @@ -4969,6 +5028,32 @@ ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS) return (sysctl_handle_string(oidp, buf, strlen(buf), req)); } +static int +ixl_handle_nvmupd_cmd(struct ixl_pf *pf, struct ifdrv *ifd) +{ + struct i40e_hw *hw = &pf->hw; + struct i40e_nvm_access *nvma; + device_t dev = pf->dev; + enum i40e_status_code status = 0; + int perrno; + + DEBUGFUNC("ixl_handle_nvmupd_cmd"); + + if (ifd->ifd_len < sizeof(struct i40e_nvm_access) || + ifd->ifd_data == NULL) { + device_printf(dev, "%s: incorrect ifdrv length or data pointer\n", __func__); + device_printf(dev, "%s: ifdrv length: %lu, sizeof(struct i40e_nvm_access): %lu\n", __func__, + ifd->ifd_len, sizeof(struct i40e_nvm_access)); + device_printf(dev, "%s: data pointer: %p\n", __func__, ifd->ifd_data); + return (EINVAL); + } + + nvma = (struct i40e_nvm_access *)ifd->ifd_data; + + status = i40e_nvmupd_command(hw, nvma, nvma->data, &perrno); + + return (status) ? perrno : 0; +} #ifdef IXL_DEBUG_SYSCTL static int @@ -5263,7 +5348,6 @@ ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS) } #endif /* IXL_DEBUG_SYSCTL */ - #ifdef PCI_IOV static int ixl_vf_alloc_vsi(struct ixl_pf *pf, struct ixl_vf *vf) diff --git a/sys/dev/ixl/ixl.h b/sys/dev/ixl/ixl.h index 09cc5ad..3bf3fe7 100644 --- a/sys/dev/ixl/ixl.h +++ b/sys/dev/ixl/ixl.h @@ -182,12 +182,6 @@ #define DBA_ALIGN 128 /* - * This parameter controls the maximum no of times the driver will loop in - * the isr. Minimum Value = 1 - */ -#define MAX_LOOP 10 - -/* * This is the max watchdog interval, ie. the time that can * pass between any two TX clean operations, such only happening * when the TX hardware is functioning. diff --git a/sys/dev/ixl/ixl_pf.h b/sys/dev/ixl/ixl_pf.h index 4f0bdda..6df0879 100644 --- a/sys/dev/ixl/ixl_pf.h +++ b/sys/dev/ixl/ixl_pf.h @@ -111,6 +111,13 @@ struct ixl_pf { int vc_debug_lvl; }; +/* + * Defines used for NVM update ioctls. + * This value is used in the Solaris tool, too. + */ +#define I40E_NVM_ACCESS \ + (((((((('E' << 4) + '1') << 4) + 'K') << 4) + 'G') << 4) | 5) + #define IXL_SET_ADVERTISE_HELP \ "Control link advertise speed:\n" \ "\tFlags:\n" \ |