diff options
Diffstat (limited to 'sys/dev/cxgbe')
| -rw-r--r-- | sys/dev/cxgbe/adapter.h | 26 | ||||
| -rw-r--r-- | sys/dev/cxgbe/common/common.h | 3 | ||||
| -rw-r--r-- | sys/dev/cxgbe/common/t4_hw.c | 1592 | ||||
| -rw-r--r-- | sys/dev/cxgbe/common/t4_msg.h | 566 | ||||
| -rw-r--r-- | sys/dev/cxgbe/common/t4_regs.h | 100 | ||||
| -rw-r--r-- | sys/dev/cxgbe/crypto/t7_kern_tls.c | 18 | ||||
| -rw-r--r-- | sys/dev/cxgbe/firmware/t4fw_interface.h | 7 | ||||
| -rw-r--r-- | sys/dev/cxgbe/nvmf/nvmf_che.c | 3331 | ||||
| -rw-r--r-- | sys/dev/cxgbe/offload.h | 3 | ||||
| -rw-r--r-- | sys/dev/cxgbe/t4_main.c | 162 | ||||
| -rw-r--r-- | sys/dev/cxgbe/t4_sge.c | 86 | ||||
| -rw-r--r-- | sys/dev/cxgbe/tom/t4_cpl_io.c | 347 | ||||
| -rw-r--r-- | sys/dev/cxgbe/tom/t4_tom.c | 6 |
13 files changed, 5754 insertions, 493 deletions
diff --git a/sys/dev/cxgbe/adapter.h b/sys/dev/cxgbe/adapter.h index 55f09fefb7e3..38875b535067 100644 --- a/sys/dev/cxgbe/adapter.h +++ b/sys/dev/cxgbe/adapter.h @@ -184,7 +184,16 @@ enum { DF_LOAD_FW_ANYTIME = (1 << 1), /* Allow LOAD_FW after init */ DF_DISABLE_TCB_CACHE = (1 << 2), /* Disable TCB cache (T6+) */ DF_DISABLE_CFG_RETRY = (1 << 3), /* Disable fallback config */ - DF_VERBOSE_SLOWINTR = (1 << 4), /* Chatty slow intr handler */ + + /* adapter intr handler flags */ + IHF_INTR_CLEAR_ON_INIT = (1 << 0), /* Driver calls t4_intr_clear */ + IHF_NO_SHOW = (1 << 1), /* Do not display intr info */ + IHF_VERBOSE = (1 << 2), /* Display extra intr info */ + IHF_FATAL_IFF_ENABLED = (1 << 3), /* Fatal only if enabled */ + IHF_IGNORE_IF_DISABLED = (1 << 4), /* Ignore if disabled */ + IHF_CLR_ALL_SET = (1 << 5), /* Clear all set bits */ + IHF_CLR_ALL_UNIGNORED = (1 << 6), /* Clear all unignored bits */ + IHF_RUN_ALL_ACTIONS = (1 << 7), /* As if all cause are set */ }; #define IS_DETACHING(vi) ((vi)->flags & VI_DETACHING) @@ -723,6 +732,16 @@ struct sge_ofld_rxq { uint64_t rx_iscsi_padding_errors; uint64_t rx_iscsi_header_digest_errors; uint64_t rx_iscsi_data_digest_errors; + counter_u64_t rx_nvme_ddp_setup_ok; + counter_u64_t rx_nvme_ddp_setup_no_stag; + counter_u64_t rx_nvme_ddp_setup_error; + counter_u64_t rx_nvme_ddp_pdus; + counter_u64_t rx_nvme_ddp_octets; + counter_u64_t rx_nvme_fl_pdus; + counter_u64_t rx_nvme_fl_octets; + counter_u64_t rx_nvme_invalid_headers; + counter_u64_t rx_nvme_header_digest_errors; + counter_u64_t rx_nvme_data_digest_errors; uint64_t rx_aio_ddp_jobs; uint64_t rx_aio_ddp_octets; u_long rx_toe_tls_records; @@ -795,6 +814,9 @@ struct sge_ofld_txq { counter_u64_t tx_iscsi_pdus; counter_u64_t tx_iscsi_octets; counter_u64_t tx_iscsi_iso_wrs; + counter_u64_t tx_nvme_pdus; + counter_u64_t tx_nvme_octets; + counter_u64_t tx_nvme_iso_wrs; counter_u64_t tx_aio_jobs; counter_u64_t tx_aio_octets; counter_u64_t tx_toe_tls_records; @@ -997,6 +1019,7 @@ struct adapter { void *iwarp_softc; /* (struct c4iw_dev *) */ struct iw_tunables iwt; void *iscsi_ulp_softc; /* (struct cxgbei_data *) */ + void *nvme_ulp_softc; /* (struct nvmf_che_adapter *) */ struct l2t_data *l2t; /* L2 table */ struct smt_data *smt; /* Source MAC Table */ struct tid_info tids; @@ -1013,6 +1036,7 @@ struct adapter { int flags; int debug_flags; int error_flags; /* Used by error handler and live reset. */ + int intr_flags; /* Used by interrupt setup/handlers. */ char ifp_lockname[16]; struct mtx ifp_lock; diff --git a/sys/dev/cxgbe/common/common.h b/sys/dev/cxgbe/common/common.h index 6b36832a7464..2033967ffb94 100644 --- a/sys/dev/cxgbe/common/common.h +++ b/sys/dev/cxgbe/common/common.h @@ -684,9 +684,10 @@ u32 t4_hw_pci_read_cfg4(adapter_t *adapter, int reg); struct fw_filter_wr; +void t4_intr_clear(struct adapter *adapter); void t4_intr_enable(struct adapter *adapter); void t4_intr_disable(struct adapter *adapter); -bool t4_slow_intr_handler(struct adapter *adapter, bool verbose); +bool t4_slow_intr_handler(struct adapter *adapter, int flags); int t4_hash_mac_addr(const u8 *addr); int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port, diff --git a/sys/dev/cxgbe/common/t4_hw.c b/sys/dev/cxgbe/common/t4_hw.c index eb7ea9acc108..65292486cbc8 100644 --- a/sys/dev/cxgbe/common/t4_hw.c +++ b/sys/dev/cxgbe/common/t4_hw.c @@ -84,6 +84,41 @@ static inline int t4_wait_op_done(struct adapter *adapter, int reg, u32 mask, delay, NULL); } + /** + * t7_wait_sram_done - wait until an operation is completed + * @adapter: the adapter performing the operation + * @reg: the register to check for completion + * @result_reg: register that holds the result value + * @attempts: number of check iterations + * @delay: delay in usecs between iterations + * @valp: where to store the value of the result register at completion time + * + * Waits until a specific bit in @reg is cleared, checking up to + * @attempts times.Once the bit is cleared, reads from @result_reg + * and stores the value in @valp if it is not NULL. Returns 0 if the + * operation completes successfully and -EAGAIN if it times out. + */ +static int t7_wait_sram_done(struct adapter *adap, int reg, int result_reg, + int attempts, int delay, u32 *valp) +{ + while (1) { + u32 val = t4_read_reg(adap, reg); + + /* Check if SramStart (bit 19) is cleared */ + if (!(val & (1 << 19))) { + if (valp) + *valp = t4_read_reg(adap, result_reg); + return 0; + } + + if (--attempts == 0) + return -EAGAIN; + + if (delay) + udelay(delay); + } +} + /** * t4_set_reg_field - set a register field to a value * @adapter: the adapter to program @@ -535,11 +570,11 @@ static int t4_edc_err_read(struct adapter *adap, int idx) edc_bist_status_rdata_reg = EDC_T5_REG(A_EDC_H_BIST_STATUS_RDATA, idx); CH_WARN(adap, - "edc%d err addr 0x%x: 0x%x.\n", + " edc%d err addr 0x%x: 0x%x.\n", idx, edc_ecc_err_addr_reg, t4_read_reg(adap, edc_ecc_err_addr_reg)); CH_WARN(adap, - "bist: 0x%x, status %llx %llx %llx %llx %llx %llx %llx %llx %llx.\n", + " bist: 0x%x, status %llx %llx %llx %llx %llx %llx %llx %llx %llx.\n", edc_bist_status_rdata_reg, (unsigned long long)t4_read_reg64(adap, edc_bist_status_rdata_reg), (unsigned long long)t4_read_reg64(adap, edc_bist_status_rdata_reg + 8), @@ -578,14 +613,15 @@ int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) mc_bist_cmd_len_reg = A_MC_BIST_CMD_LEN; mc_bist_status_rdata_reg = A_MC_BIST_STATUS_RDATA; mc_bist_data_pattern_reg = A_MC_BIST_DATA_PATTERN; - } else { + } else if (chip_id(adap) < CHELSIO_T7) { mc_bist_cmd_reg = MC_REG(A_MC_P_BIST_CMD, idx); mc_bist_cmd_addr_reg = MC_REG(A_MC_P_BIST_CMD_ADDR, idx); mc_bist_cmd_len_reg = MC_REG(A_MC_P_BIST_CMD_LEN, idx); - mc_bist_status_rdata_reg = MC_REG(A_MC_P_BIST_STATUS_RDATA, - idx); - mc_bist_data_pattern_reg = MC_REG(A_MC_P_BIST_DATA_PATTERN, - idx); + mc_bist_status_rdata_reg = MC_REG(A_MC_P_BIST_STATUS_RDATA, idx); + mc_bist_data_pattern_reg = MC_REG(A_MC_P_BIST_DATA_PATTERN, idx); + } else { + /* Need to figure out split mode and the rest. */ + return (-ENOTSUP); } if (t4_read_reg(adap, mc_bist_cmd_reg) & F_START_BIST) @@ -636,21 +672,13 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) edc_bist_status_rdata_reg = EDC_REG(A_EDC_BIST_STATUS_RDATA, idx); } else { -/* - * These macro are missing in t4_regs.h file. - * Added temporarily for testing. - */ -#define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR) -#define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx) - edc_bist_cmd_reg = EDC_REG_T5(A_EDC_H_BIST_CMD, idx); - edc_bist_cmd_addr_reg = EDC_REG_T5(A_EDC_H_BIST_CMD_ADDR, idx); - edc_bist_cmd_len_reg = EDC_REG_T5(A_EDC_H_BIST_CMD_LEN, idx); - edc_bist_cmd_data_pattern = EDC_REG_T5(A_EDC_H_BIST_DATA_PATTERN, + edc_bist_cmd_reg = EDC_T5_REG(A_EDC_H_BIST_CMD, idx); + edc_bist_cmd_addr_reg = EDC_T5_REG(A_EDC_H_BIST_CMD_ADDR, idx); + edc_bist_cmd_len_reg = EDC_T5_REG(A_EDC_H_BIST_CMD_LEN, idx); + edc_bist_cmd_data_pattern = EDC_T5_REG(A_EDC_H_BIST_DATA_PATTERN, idx); - edc_bist_status_rdata_reg = EDC_REG_T5(A_EDC_H_BIST_STATUS_RDATA, + edc_bist_status_rdata_reg = EDC_T5_REG(A_EDC_H_BIST_STATUS_RDATA, idx); -#undef EDC_REG_T5 -#undef EDC_STRIDE_T5 } if (t4_read_reg(adap, edc_bist_cmd_reg) & F_START_BIST) @@ -2662,10 +2690,9 @@ void t4_get_regs(struct adapter *adap, u8 *buf, size_t buf_size) 0x173c, 0x1760, 0x1800, 0x18fc, 0x3000, 0x3044, - 0x3060, 0x3064, 0x30a4, 0x30b0, 0x30b8, 0x30d8, - 0x30e0, 0x30fc, + 0x30e0, 0x30e8, 0x3140, 0x357c, 0x35a8, 0x35cc, 0x35e0, 0x35ec, @@ -2680,7 +2707,7 @@ void t4_get_regs(struct adapter *adap, u8 *buf, size_t buf_size) 0x480c, 0x4814, 0x4890, 0x489c, 0x48a4, 0x48ac, - 0x48b8, 0x48c4, + 0x48b8, 0x48bc, 0x4900, 0x4924, 0x4ffc, 0x4ffc, 0x5500, 0x5624, @@ -2698,8 +2725,10 @@ void t4_get_regs(struct adapter *adap, u8 *buf, size_t buf_size) 0x5a60, 0x5a6c, 0x5a80, 0x5a8c, 0x5a94, 0x5a9c, - 0x5b94, 0x5bfc, - 0x5c10, 0x5e48, + 0x5b94, 0x5bec, + 0x5bf8, 0x5bfc, + 0x5c10, 0x5c40, + 0x5c4c, 0x5e48, 0x5e50, 0x5e94, 0x5ea0, 0x5eb0, 0x5ec0, 0x5ec0, @@ -2708,7 +2737,8 @@ void t4_get_regs(struct adapter *adap, u8 *buf, size_t buf_size) 0x5ef0, 0x5ef0, 0x5f00, 0x5f04, 0x5f0c, 0x5f10, - 0x5f20, 0x5f88, + 0x5f20, 0x5f78, + 0x5f84, 0x5f88, 0x5f90, 0x5fd8, 0x6000, 0x6020, 0x6028, 0x6030, @@ -3084,7 +3114,7 @@ void t4_get_regs(struct adapter *adap, u8 *buf, size_t buf_size) 0x38140, 0x38140, 0x38150, 0x38154, 0x38160, 0x381c4, - 0x381f0, 0x38204, + 0x381d0, 0x38204, 0x3820c, 0x38214, 0x3821c, 0x3822c, 0x38244, 0x38244, @@ -3156,6 +3186,10 @@ void t4_get_regs(struct adapter *adap, u8 *buf, size_t buf_size) 0x3a000, 0x3a004, 0x3a050, 0x3a084, 0x3a090, 0x3a09c, + 0x3a93c, 0x3a93c, + 0x3b93c, 0x3b93c, + 0x3c93c, 0x3c93c, + 0x3d93c, 0x3d93c, 0x3e000, 0x3e020, 0x3e03c, 0x3e05c, 0x3e100, 0x3e120, @@ -4743,10 +4777,9 @@ struct intr_details { struct intr_action { u32 mask; int arg; - bool (*action)(struct adapter *, int, bool); + bool (*action)(struct adapter *, int, int); }; -#define NONFATAL_IF_DISABLED 1 struct intr_info { const char *name; /* name of the INT_CAUSE register */ int cause_reg; /* INT_CAUSE register */ @@ -4769,73 +4802,78 @@ intr_alert_char(u32 cause, u32 enable, u32 fatal) } static void -t4_show_intr_info(struct adapter *adap, const struct intr_info *ii, u32 cause) +show_intr_info(struct adapter *sc, const struct intr_info *ii, uint32_t cause, + uint32_t ucause, uint32_t enabled, uint32_t fatal, int flags) { - u32 enable, fatal, leftover; + uint32_t leftover, msgbits; const struct intr_details *details; char alert; + const bool verbose = flags & IHF_VERBOSE; - enable = t4_read_reg(adap, ii->enable_reg); - if (ii->flags & NONFATAL_IF_DISABLED) - fatal = ii->fatal & t4_read_reg(adap, ii->enable_reg); - else - fatal = ii->fatal; - alert = intr_alert_char(cause, enable, fatal); - CH_ALERT(adap, "%c %s 0x%x = 0x%08x, E 0x%08x, F 0x%08x\n", - alert, ii->name, ii->cause_reg, cause, enable, fatal); + if (verbose || ucause != 0 || flags & IHF_RUN_ALL_ACTIONS) { + alert = intr_alert_char(cause, enabled, fatal); + CH_ALERT(sc, "%c %s 0x%x = 0x%08x, E 0x%08x, F 0x%08x\n", alert, + ii->name, ii->cause_reg, cause, enabled, fatal); + } - leftover = cause; + leftover = verbose ? cause : ucause; for (details = ii->details; details && details->mask != 0; details++) { - u32 msgbits = details->mask & cause; + msgbits = details->mask & leftover; if (msgbits == 0) continue; - alert = intr_alert_char(msgbits, enable, ii->fatal); - CH_ALERT(adap, " %c [0x%08x] %s\n", alert, msgbits, - details->msg); + alert = intr_alert_char(msgbits, enabled, fatal); + CH_ALERT(sc, " %c [0x%08x] %s\n", alert, msgbits, details->msg); leftover &= ~msgbits; } - if (leftover != 0 && leftover != cause) - CH_ALERT(adap, " ? [0x%08x]\n", leftover); + if (leftover != 0 && leftover != (verbose ? cause : ucause)) + CH_ALERT(sc, " ? [0x%08x]\n", leftover); } /* * Returns true for fatal error. */ static bool -t4_handle_intr(struct adapter *adap, const struct intr_info *ii, - u32 additional_cause, bool verbose) +t4_handle_intr(struct adapter *sc, const struct intr_info *ii, uint32_t acause, + int flags) { - u32 cause, fatal; + uint32_t cause, ucause, enabled, fatal; bool rc; const struct intr_action *action; - /* - * Read and display cause. Note that the top level PL_INT_CAUSE is a - * bit special and we need to completely ignore the bits that are not in - * PL_INT_ENABLE. - */ - cause = t4_read_reg(adap, ii->cause_reg); - if (ii->cause_reg == A_PL_INT_CAUSE) - cause &= t4_read_reg(adap, ii->enable_reg); - if (verbose || cause != 0) - t4_show_intr_info(adap, ii, cause); - fatal = cause & ii->fatal; - if (fatal != 0 && ii->flags & NONFATAL_IF_DISABLED) - fatal &= t4_read_reg(adap, ii->enable_reg); - cause |= additional_cause; - if (cause == 0) - return (false); + cause = t4_read_reg(sc, ii->cause_reg); + enabled = t4_read_reg(sc, ii->enable_reg); + flags |= ii->flags; + fatal = ii->fatal & cause; + if (flags & IHF_FATAL_IFF_ENABLED) + fatal &= enabled; + ucause = cause; + if (flags & IHF_IGNORE_IF_DISABLED) + ucause &= enabled; + if (!(flags & IHF_NO_SHOW)) + show_intr_info(sc, ii, cause, ucause, enabled, fatal, flags); rc = fatal != 0; for (action = ii->actions; action && action->mask != 0; action++) { - if (!(action->mask & cause)) + if (action->action == NULL) continue; - rc |= (action->action)(adap, action->arg, verbose); + if (action->mask & (ucause | acause) || + flags & IHF_RUN_ALL_ACTIONS) { + bool rc1 = (action->action)(sc, action->arg, flags); + if (action->mask & ucause) + rc |= rc1; + } } /* clear */ - t4_write_reg(adap, ii->cause_reg, cause); - (void)t4_read_reg(adap, ii->cause_reg); + if (cause != 0) { + if (flags & IHF_CLR_ALL_SET) { + t4_write_reg(sc, ii->cause_reg, cause); + (void)t4_read_reg(sc, ii->cause_reg); + } else if (ucause != 0 && flags & IHF_CLR_ALL_UNIGNORED) { + t4_write_reg(sc, ii->cause_reg, ucause); + (void)t4_read_reg(sc, ii->cause_reg); + } + } return (rc); } @@ -4843,7 +4881,7 @@ t4_handle_intr(struct adapter *adap, const struct intr_info *ii, /* * Interrupt handler for the PCIE module. */ -static bool pcie_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool pcie_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_details sysbus_intr_details[] = { { F_RNPP, "RXNP array parity error" }, @@ -4956,21 +4994,43 @@ static bool pcie_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_PCIE_INT_CAUSE, .enable_reg = A_PCIE_INT_ENABLE, .fatal = 0xffffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + struct intr_info pcie_int_cause_ext = { + .name = "PCIE_INT_CAUSE_EXT", + .cause_reg = A_PCIE_INT_CAUSE_EXT, + .enable_reg = A_PCIE_INT_ENABLE_EXT, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + struct intr_info pcie_int_cause_x8 = { + .name = "PCIE_INT_CAUSE_X8", + .cause_reg = A_PCIE_INT_CAUSE_X8, + .enable_reg = A_PCIE_INT_ENABLE_X8, + .fatal = 0, + .flags = 0, .details = NULL, .actions = NULL, }; bool fatal = false; if (is_t4(adap)) { - fatal |= t4_handle_intr(adap, &sysbus_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &pcie_port_intr_info, 0, verbose); + fatal |= t4_handle_intr(adap, &sysbus_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &pcie_port_intr_info, 0, flags); pcie_intr_info.details = pcie_intr_details; } else { pcie_intr_info.details = t5_pcie_intr_details; } - fatal |= t4_handle_intr(adap, &pcie_intr_info, 0, verbose); + fatal |= t4_handle_intr(adap, &pcie_intr_info, 0, flags); + if (chip_id(adap) > CHELSIO_T6) { + fatal |= t4_handle_intr(adap, &pcie_int_cause_ext, 0, flags); + fatal |= t4_handle_intr(adap, &pcie_int_cause_x8, 0, flags); + } return (fatal); } @@ -4978,7 +5038,7 @@ static bool pcie_intr_handler(struct adapter *adap, int arg, bool verbose) /* * TP interrupt handler. */ -static bool tp_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool tp_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_details tp_intr_details[] = { { 0x3fffffff, "TP parity error" }, @@ -4990,25 +5050,90 @@ static bool tp_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_TP_INT_CAUSE, .enable_reg = A_TP_INT_ENABLE, .fatal = 0x7fffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = tp_intr_details, .actions = NULL, }; + static const struct intr_info tp_inic_perr_cause = { + .name = "TP_INIC_PERR_CAUSE", + .cause_reg = A_TP_INIC_PERR_CAUSE, + .enable_reg = A_TP_INIC_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info tp_c_perr_cause = { + .name = "TP_C_PERR_CAUSE", + .cause_reg = A_TP_C_PERR_CAUSE, + .enable_reg = A_TP_C_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info tp_e_eg_perr_cause = { + .name = "TP_E_EG_PERR_CAUSE", + .cause_reg = A_TP_E_EG_PERR_CAUSE, + .enable_reg = A_TP_E_EG_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info tp_e_in0_perr_cause = { + .name = "TP_E_IN0_PERR_CAUSE", + .cause_reg = A_TP_E_IN0_PERR_CAUSE, + .enable_reg = A_TP_E_IN0_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info tp_e_in1_perr_cause = { + .name = "TP_E_IN1_PERR_CAUSE", + .cause_reg = A_TP_E_IN1_PERR_CAUSE, + .enable_reg = A_TP_E_IN1_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info tp_o_perr_cause = { + .name = "TP_O_PERR_CAUSE", + .cause_reg = A_TP_O_PERR_CAUSE, + .enable_reg = A_TP_O_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + bool fatal; + + fatal = t4_handle_intr(adap, &tp_intr_info, 0, flags); + if (chip_id(adap) > CHELSIO_T6) { + fatal |= t4_handle_intr(adap, &tp_inic_perr_cause, 0, flags); + fatal |= t4_handle_intr(adap, &tp_c_perr_cause, 0, flags); + fatal |= t4_handle_intr(adap, &tp_e_eg_perr_cause, 0, flags); + fatal |= t4_handle_intr(adap, &tp_e_in0_perr_cause, 0, flags); + fatal |= t4_handle_intr(adap, &tp_e_in1_perr_cause, 0, flags); + fatal |= t4_handle_intr(adap, &tp_o_perr_cause, 0, flags); + } - return (t4_handle_intr(adap, &tp_intr_info, 0, verbose)); + return (fatal); } /* * SGE interrupt handler. */ -static bool sge_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool sge_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_info sge_int1_info = { .name = "SGE_INT_CAUSE1", .cause_reg = A_SGE_INT_CAUSE1, .enable_reg = A_SGE_INT_ENABLE1, .fatal = 0xffffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = NULL, .actions = NULL, }; @@ -5017,7 +5142,7 @@ static bool sge_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_SGE_INT_CAUSE2, .enable_reg = A_SGE_INT_ENABLE2, .fatal = 0xffffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = NULL, .actions = NULL, }; @@ -5115,7 +5240,7 @@ static bool sge_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_SGE_INT_CAUSE5, .enable_reg = A_SGE_INT_ENABLE5, .fatal = 0xffffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = NULL, .actions = NULL, }; @@ -5128,7 +5253,24 @@ static bool sge_intr_handler(struct adapter *adap, int arg, bool verbose) .details = NULL, .actions = NULL, }; - + static const struct intr_info sge_int7_info = { + .name = "SGE_INT_CAUSE7", + .cause_reg = A_SGE_INT_CAUSE7, + .enable_reg = A_SGE_INT_ENABLE7, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info sge_int8_info = { + .name = "SGE_INT_CAUSE8", + .cause_reg = A_SGE_INT_CAUSE8, + .enable_reg = A_SGE_INT_ENABLE8, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; bool fatal; u32 v; @@ -5139,14 +5281,18 @@ static bool sge_intr_handler(struct adapter *adap, int arg, bool verbose) } fatal = false; - fatal |= t4_handle_intr(adap, &sge_int1_info, 0, verbose); - fatal |= t4_handle_intr(adap, &sge_int2_info, 0, verbose); - fatal |= t4_handle_intr(adap, &sge_int3_info, 0, verbose); - fatal |= t4_handle_intr(adap, &sge_int4_info, 0, verbose); + fatal |= t4_handle_intr(adap, &sge_int1_info, 0, flags); + fatal |= t4_handle_intr(adap, &sge_int2_info, 0, flags); + fatal |= t4_handle_intr(adap, &sge_int3_info, 0, flags); + fatal |= t4_handle_intr(adap, &sge_int4_info, 0, flags); if (chip_id(adap) >= CHELSIO_T5) - fatal |= t4_handle_intr(adap, &sge_int5_info, 0, verbose); + fatal |= t4_handle_intr(adap, &sge_int5_info, 0, flags); if (chip_id(adap) >= CHELSIO_T6) - fatal |= t4_handle_intr(adap, &sge_int6_info, 0, verbose); + fatal |= t4_handle_intr(adap, &sge_int6_info, 0, flags); + if (chip_id(adap) >= CHELSIO_T7) { + fatal |= t4_handle_intr(adap, &sge_int7_info, 0, flags); + fatal |= t4_handle_intr(adap, &sge_int8_info, 0, flags); + } v = t4_read_reg(adap, A_SGE_ERROR_STATS); if (v & F_ERROR_QID_VALID) { @@ -5163,7 +5309,7 @@ static bool sge_intr_handler(struct adapter *adap, int arg, bool verbose) /* * CIM interrupt handler. */ -static bool cim_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool cim_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_details cim_host_intr_details[] = { /* T6+ */ @@ -5208,7 +5354,7 @@ static bool cim_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_CIM_HOST_INT_CAUSE, .enable_reg = A_CIM_HOST_INT_ENABLE, .fatal = 0x007fffe6, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = cim_host_intr_details, .actions = NULL, }; @@ -5259,7 +5405,7 @@ static bool cim_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_CIM_HOST_UPACC_INT_CAUSE, .enable_reg = A_CIM_HOST_UPACC_INT_ENABLE, .fatal = 0x3fffeeff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = cim_host_upacc_intr_details, .actions = NULL, }; @@ -5272,6 +5418,15 @@ static bool cim_intr_handler(struct adapter *adap, int arg, bool verbose) .details = NULL, .actions = NULL, }; + static const struct intr_info cim_perr_cause = { + .name = "CIM_PERR_CAUSE", + .cause_reg = A_CIM_PERR_CAUSE, + .enable_reg = A_CIM_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; u32 val, fw_err; bool fatal; @@ -5290,9 +5445,11 @@ static bool cim_intr_handler(struct adapter *adap, int arg, bool verbose) } fatal = (fw_err & F_PCIE_FW_ERR) != 0; - fatal |= t4_handle_intr(adap, &cim_host_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &cim_host_upacc_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &cim_pf_host_intr_info, 0, verbose); + fatal |= t4_handle_intr(adap, &cim_host_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &cim_host_upacc_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &cim_pf_host_intr_info, 0, flags); + if (chip_id(adap) > CHELSIO_T6) + fatal |= t4_handle_intr(adap, &cim_perr_cause, 0, flags); if (fatal) t4_os_cim_err(adap); @@ -5302,7 +5459,7 @@ static bool cim_intr_handler(struct adapter *adap, int arg, bool verbose) /* * ULP RX interrupt handler. */ -static bool ulprx_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool ulprx_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_details ulprx_intr_details[] = { /* T5+ */ @@ -5320,7 +5477,7 @@ static bool ulprx_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_ULP_RX_INT_CAUSE, .enable_reg = A_ULP_RX_INT_ENABLE, .fatal = 0x07ffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = ulprx_intr_details, .actions = NULL, }; @@ -5333,10 +5490,53 @@ static bool ulprx_intr_handler(struct adapter *adap, int arg, bool verbose) .details = NULL, .actions = NULL, }; + static const struct intr_info ulprx_int_cause_pcmd = { + .name = "ULP_RX_INT_CAUSE_PCMD", + .cause_reg = A_ULP_RX_INT_CAUSE_PCMD, + .enable_reg = A_ULP_RX_INT_ENABLE_PCMD, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulprx_int_cause_data = { + .name = "ULP_RX_INT_CAUSE_DATA", + .cause_reg = A_ULP_RX_INT_CAUSE_DATA, + .enable_reg = A_ULP_RX_INT_ENABLE_DATA, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulprx_int_cause_arb = { + .name = "ULP_RX_INT_CAUSE_ARB", + .cause_reg = A_ULP_RX_INT_CAUSE_ARB, + .enable_reg = A_ULP_RX_INT_ENABLE_ARB, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulprx_int_cause_intf = { + .name = "ULP_RX_INT_CAUSE_INTERFACE", + .cause_reg = A_ULP_RX_INT_CAUSE_INTERFACE, + .enable_reg = A_ULP_RX_INT_ENABLE_INTERFACE, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; bool fatal = false; - fatal |= t4_handle_intr(adap, &ulprx_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &ulprx_intr2_info, 0, verbose); + fatal |= t4_handle_intr(adap, &ulprx_intr_info, 0, flags); + if (chip_id(adap) < CHELSIO_T7) + fatal |= t4_handle_intr(adap, &ulprx_intr2_info, 0, flags); + else { + fatal |= t4_handle_intr(adap, &ulprx_int_cause_pcmd, 0, flags); + fatal |= t4_handle_intr(adap, &ulprx_int_cause_data, 0, flags); + fatal |= t4_handle_intr(adap, &ulprx_int_cause_arb, 0, flags); + fatal |= t4_handle_intr(adap, &ulprx_int_cause_intf, 0, flags); + } return (fatal); } @@ -5344,7 +5544,7 @@ static bool ulprx_intr_handler(struct adapter *adap, int arg, bool verbose) /* * ULP TX interrupt handler. */ -static bool ulptx_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool ulptx_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_details ulptx_intr_details[] = { { F_PBL_BOUND_ERR_CH3, "ULPTX channel 3 PBL out of bounds" }, @@ -5359,32 +5559,98 @@ static bool ulptx_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_ULP_TX_INT_CAUSE, .enable_reg = A_ULP_TX_INT_ENABLE, .fatal = 0x0fffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = ulptx_intr_details, .actions = NULL, }; - static const struct intr_info ulptx_intr2_info = { + static const struct intr_info ulptx_intr_info2 = { .name = "ULP_TX_INT_CAUSE_2", .cause_reg = A_ULP_TX_INT_CAUSE_2, .enable_reg = A_ULP_TX_INT_ENABLE_2, - .fatal = 0xf0, - .flags = NONFATAL_IF_DISABLED, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulptx_intr_info3 = { + .name = "ULP_TX_INT_CAUSE_3", + .cause_reg = A_ULP_TX_INT_CAUSE_3, + .enable_reg = A_ULP_TX_INT_ENABLE_3, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulptx_intr_info4 = { + .name = "ULP_TX_INT_CAUSE_4", + .cause_reg = A_ULP_TX_INT_CAUSE_4, + .enable_reg = A_ULP_TX_INT_ENABLE_4, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulptx_intr_info5 = { + .name = "ULP_TX_INT_CAUSE_5", + .cause_reg = A_ULP_TX_INT_CAUSE_5, + .enable_reg = A_ULP_TX_INT_ENABLE_5, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulptx_intr_info6 = { + .name = "ULP_TX_INT_CAUSE_6", + .cause_reg = A_ULP_TX_INT_CAUSE_6, + .enable_reg = A_ULP_TX_INT_ENABLE_6, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulptx_intr_info7 = { + .name = "ULP_TX_INT_CAUSE_7", + .cause_reg = A_ULP_TX_INT_CAUSE_7, + .enable_reg = A_ULP_TX_INT_ENABLE_7, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info ulptx_intr_info8 = { + .name = "ULP_TX_INT_CAUSE_8", + .cause_reg = A_ULP_TX_INT_CAUSE_8, + .enable_reg = A_ULP_TX_INT_ENABLE_8, + .fatal = 0, + .flags = 0, .details = NULL, .actions = NULL, }; bool fatal = false; - fatal |= t4_handle_intr(adap, &ulptx_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &ulptx_intr2_info, 0, verbose); + fatal |= t4_handle_intr(adap, &ulptx_intr_info, 0, flags); + if (chip_id(adap) > CHELSIO_T4) + fatal |= t4_handle_intr(adap, &ulptx_intr_info2, 0, flags); + if (chip_id(adap) > CHELSIO_T6) { + fatal |= t4_handle_intr(adap, &ulptx_intr_info3, 0, flags); + fatal |= t4_handle_intr(adap, &ulptx_intr_info4, 0, flags); + fatal |= t4_handle_intr(adap, &ulptx_intr_info5, 0, flags); + fatal |= t4_handle_intr(adap, &ulptx_intr_info6, 0, flags); + fatal |= t4_handle_intr(adap, &ulptx_intr_info7, 0, flags); + fatal |= t4_handle_intr(adap, &ulptx_intr_info8, 0, flags); + } return (fatal); } -static bool pmtx_dump_dbg_stats(struct adapter *adap, int arg, bool verbose) +static bool pmtx_dump_dbg_stats(struct adapter *adap, int arg, int flags) { int i; u32 data[17]; + if (flags & IHF_NO_SHOW) + return (false); + t4_read_indirect(adap, A_PM_TX_DBG_CTRL, A_PM_TX_DBG_DATA, &data[0], ARRAY_SIZE(data), A_PM_TX_DBG_STAT0); for (i = 0; i < ARRAY_SIZE(data); i++) { @@ -5398,13 +5664,9 @@ static bool pmtx_dump_dbg_stats(struct adapter *adap, int arg, bool verbose) /* * PM TX interrupt handler. */ -static bool pmtx_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool pmtx_intr_handler(struct adapter *adap, int arg, int flags) { - static const struct intr_action pmtx_intr_actions[] = { - { 0xffffffff, 0, pmtx_dump_dbg_stats }, - { 0 }, - }; - static const struct intr_details pmtx_intr_details[] = { + static const struct intr_details pmtx_int_cause_fields[] = { { F_PCMD_LEN_OVFL0, "PMTX channel 0 pcmd too large" }, { F_PCMD_LEN_OVFL1, "PMTX channel 1 pcmd too large" }, { F_PCMD_LEN_OVFL2, "PMTX channel 2 pcmd too large" }, @@ -5421,25 +5683,29 @@ static bool pmtx_intr_handler(struct adapter *adap, int arg, bool verbose) { F_C_PCMD_PAR_ERROR, "PMTX c_pcmd parity error" }, { 0 } }; - static const struct intr_info pmtx_intr_info = { + static const struct intr_action pmtx_int_cause_actions[] = { + { 0xffffffff, -1, pmtx_dump_dbg_stats }, + { 0 }, + }; + static const struct intr_info pmtx_int_cause = { .name = "PM_TX_INT_CAUSE", .cause_reg = A_PM_TX_INT_CAUSE, .enable_reg = A_PM_TX_INT_ENABLE, .fatal = 0xffffffff, .flags = 0, - .details = pmtx_intr_details, - .actions = pmtx_intr_actions, + .details = pmtx_int_cause_fields, + .actions = pmtx_int_cause_actions, }; - return (t4_handle_intr(adap, &pmtx_intr_info, 0, verbose)); + return (t4_handle_intr(adap, &pmtx_int_cause, 0, flags)); } /* * PM RX interrupt handler. */ -static bool pmrx_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool pmrx_intr_handler(struct adapter *adap, int arg, int flags) { - static const struct intr_details pmrx_intr_details[] = { + static const struct intr_details pmrx_int_cause_fields[] = { /* T6+ */ { 0x18000000, "PMRX ospi overflow" }, { F_MA_INTF_SDC_ERR, "PMRX MA interface SDC parity error" }, @@ -5461,25 +5727,25 @@ static bool pmrx_intr_handler(struct adapter *adap, int arg, bool verbose) { F_E_PCMD_PAR_ERROR, "PMRX e_pcmd parity error"}, { 0 } }; - static const struct intr_info pmrx_intr_info = { + static const struct intr_info pmrx_int_cause = { .name = "PM_RX_INT_CAUSE", .cause_reg = A_PM_RX_INT_CAUSE, .enable_reg = A_PM_RX_INT_ENABLE, .fatal = 0x1fffffff, - .flags = NONFATAL_IF_DISABLED, - .details = pmrx_intr_details, + .flags = IHF_FATAL_IFF_ENABLED, + .details = pmrx_int_cause_fields, .actions = NULL, }; - return (t4_handle_intr(adap, &pmrx_intr_info, 0, verbose)); + return (t4_handle_intr(adap, &pmrx_int_cause, 0, flags)); } /* * CPL switch interrupt handler. */ -static bool cplsw_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool cplsw_intr_handler(struct adapter *adap, int arg, int flags) { - static const struct intr_details cplsw_intr_details[] = { + static const struct intr_details cplsw_int_cause_fields[] = { /* T5+ */ { F_PERR_CPL_128TO128_1, "CPLSW 128TO128 FIFO1 parity error" }, { F_PERR_CPL_128TO128_0, "CPLSW 128TO128 FIFO0 parity error" }, @@ -5493,17 +5759,17 @@ static bool cplsw_intr_handler(struct adapter *adap, int arg, bool verbose) { F_ZERO_SWITCH_ERROR, "CPLSW no-switch error" }, { 0 } }; - static const struct intr_info cplsw_intr_info = { + static const struct intr_info cplsw_int_cause = { .name = "CPL_INTR_CAUSE", .cause_reg = A_CPL_INTR_CAUSE, .enable_reg = A_CPL_INTR_ENABLE, - .fatal = 0xff, - .flags = NONFATAL_IF_DISABLED, - .details = cplsw_intr_details, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = cplsw_int_cause_fields, .actions = NULL, }; - return (t4_handle_intr(adap, &cplsw_intr_info, 0, verbose)); + return (t4_handle_intr(adap, &cplsw_int_cause, 0, flags)); } #define T4_LE_FATAL_MASK (F_PARITYERR | F_UNKNOWNCMD | F_REQQPARERR) @@ -5515,11 +5781,12 @@ static bool cplsw_intr_handler(struct adapter *adap, int arg, bool verbose) #define T6_LE_FATAL_MASK (T6_LE_PERRCRC_MASK | F_T6_UNKNOWNCMD | \ F_TCAMACCFAIL | F_HASHTBLACCFAIL | F_CMDTIDERR | F_CMDPRSRINTERR | \ F_TOTCNTERR | F_CLCAMFIFOERR | F_CLIPSUBERR) +#define T7_LE_FATAL_MASK (T6_LE_FATAL_MASK | F_CACHESRAMPERR | F_CACHEINTPERR) /* * LE interrupt handler. */ -static bool le_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool le_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_details le_intr_details[] = { { F_REQQPARERR, "LE request queue parity error" }, @@ -5556,7 +5823,7 @@ static bool le_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_LE_DB_INT_CAUSE, .enable_reg = A_LE_DB_INT_ENABLE, .fatal = 0, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = NULL, .actions = NULL, }; @@ -5566,16 +5833,19 @@ static bool le_intr_handler(struct adapter *adap, int arg, bool verbose) le_intr_info.fatal = T5_LE_FATAL_MASK; } else { le_intr_info.details = t6_le_intr_details; - le_intr_info.fatal = T6_LE_FATAL_MASK; + if (chip_id(adap) < CHELSIO_T7) + le_intr_info.fatal = T6_LE_FATAL_MASK; + else + le_intr_info.fatal = T7_LE_FATAL_MASK; } - return (t4_handle_intr(adap, &le_intr_info, 0, verbose)); + return (t4_handle_intr(adap, &le_intr_info, 0, flags)); } /* * MPS interrupt handler. */ -static bool mps_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool mps_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_details mps_rx_perr_intr_details[] = { { 0xffffffff, "MPS Rx parity error" }, @@ -5586,10 +5856,55 @@ static bool mps_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_MPS_RX_PERR_INT_CAUSE, .enable_reg = A_MPS_RX_PERR_INT_ENABLE, .fatal = 0xffffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = mps_rx_perr_intr_details, .actions = NULL, }; + static const struct intr_info mps_rx_perr_intr_info2 = { + .name = "MPS_RX_PERR_INT_CAUSE2", + .cause_reg = A_MPS_RX_PERR_INT_CAUSE2, + .enable_reg = A_MPS_RX_PERR_INT_ENABLE2, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mps_rx_perr_intr_info3 = { + .name = "MPS_RX_PERR_INT_CAUSE3", + .cause_reg = A_MPS_RX_PERR_INT_CAUSE3, + .enable_reg = A_MPS_RX_PERR_INT_ENABLE3, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mps_rx_perr_intr_info4 = { + .name = "MPS_RX_PERR_INT_CAUSE4", + .cause_reg = A_MPS_RX_PERR_INT_CAUSE4, + .enable_reg = A_MPS_RX_PERR_INT_ENABLE4, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mps_rx_perr_intr_info5 = { + .name = "MPS_RX_PERR_INT_CAUSE5", + .cause_reg = A_MPS_RX_PERR_INT_CAUSE5, + .enable_reg = A_MPS_RX_PERR_INT_ENABLE5, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mps_rx_perr_intr_info6 = { + .name = "MPS_RX_PERR_INT_CAUSE6", + .cause_reg = A_MPS_RX_PERR_INT_CAUSE6, + .enable_reg = A_MPS_RX_PERR_INT_ENABLE6, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; static const struct intr_details mps_tx_intr_details[] = { { F_PORTERR, "MPS Tx destination port is disabled" }, { F_FRMERR, "MPS Tx framing error" }, @@ -5606,10 +5921,37 @@ static bool mps_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_MPS_TX_INT_CAUSE, .enable_reg = A_MPS_TX_INT_ENABLE, .fatal = 0x1ffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = mps_tx_intr_details, .actions = NULL, }; + static const struct intr_info mps_tx_intr_info2 = { + .name = "MPS_TX_INT2_CAUSE", + .cause_reg = A_MPS_TX_INT2_CAUSE, + .enable_reg = A_MPS_TX_INT2_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mps_tx_intr_info3 = { + .name = "MPS_TX_INT3_CAUSE", + .cause_reg = A_MPS_TX_INT3_CAUSE, + .enable_reg = A_MPS_TX_INT3_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mps_tx_intr_info4 = { + .name = "MPS_TX_INT4_CAUSE", + .cause_reg = A_MPS_TX_INT4_CAUSE, + .enable_reg = A_MPS_TX_INT4_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; static const struct intr_details mps_trc_intr_details[] = { { F_MISCPERR, "MPS TRC misc parity error" }, { V_PKTFIFO(M_PKTFIFO), "MPS TRC packet FIFO parity error" }, @@ -5626,14 +5968,23 @@ static bool mps_intr_handler(struct adapter *adap, int arg, bool verbose) .actions = NULL, }; static const struct intr_info t7_mps_trc_intr_info = { - .name = "T7_MPS_TRC_INT_CAUSE", + .name = "MPS_TRC_INT_CAUSE", .cause_reg = A_T7_MPS_TRC_INT_CAUSE, .enable_reg = A_T7_MPS_TRC_INT_ENABLE, - .fatal = F_MISCPERR | V_PKTFIFO(M_PKTFIFO) | V_FILTMEM(M_FILTMEM), - .flags = 0, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, .details = mps_trc_intr_details, .actions = NULL, }; + static const struct intr_info t7_mps_trc_intr_info2 = { + .name = "MPS_TRC_INT_CAUSE2", + .cause_reg = A_MPS_TRC_INT_CAUSE2, + .enable_reg = A_MPS_TRC_INT_ENABLE2, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; static const struct intr_details mps_stat_sram_intr_details[] = { { 0xffffffff, "MPS statistics SRAM parity error" }, { 0 } @@ -5643,7 +5994,7 @@ static bool mps_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_MPS_STAT_PERR_INT_CAUSE_SRAM, .enable_reg = A_MPS_STAT_PERR_INT_ENABLE_SRAM, .fatal = 0x1fffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = mps_stat_sram_intr_details, .actions = NULL, }; @@ -5656,7 +6007,7 @@ static bool mps_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_MPS_STAT_PERR_INT_CAUSE_TX_FIFO, .enable_reg = A_MPS_STAT_PERR_INT_ENABLE_TX_FIFO, .fatal = 0xffffff, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = mps_stat_tx_intr_details, .actions = NULL, }; @@ -5701,24 +6052,31 @@ static bool mps_intr_handler(struct adapter *adap, int arg, bool verbose) .details = mps_stat_sram1_intr_details, .actions = NULL, }; + bool fatal = false; - bool fatal; - - fatal = false; - fatal |= t4_handle_intr(adap, &mps_rx_perr_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &mps_tx_intr_info, 0, verbose); - if (chip_id(adap) > CHELSIO_T6) - fatal |= t4_handle_intr(adap, &t7_mps_trc_intr_info, 0, verbose); - else - fatal |= t4_handle_intr(adap, &mps_trc_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &mps_stat_sram_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &mps_stat_tx_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &mps_stat_rx_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &mps_cls_intr_info, 0, verbose); - if (chip_id(adap) > CHELSIO_T4) { - fatal |= t4_handle_intr(adap, &mps_stat_sram1_intr_info, 0, - verbose); + fatal |= t4_handle_intr(adap, &mps_rx_perr_intr_info, 0, flags); + if (chip_id(adap) > CHELSIO_T6) { + fatal |= t4_handle_intr(adap, &mps_rx_perr_intr_info2, 0, flags); + fatal |= t4_handle_intr(adap, &mps_rx_perr_intr_info3, 0, flags); + fatal |= t4_handle_intr(adap, &mps_rx_perr_intr_info4, 0, flags); + fatal |= t4_handle_intr(adap, &mps_rx_perr_intr_info5, 0, flags); + fatal |= t4_handle_intr(adap, &mps_rx_perr_intr_info6, 0, flags); } + fatal |= t4_handle_intr(adap, &mps_tx_intr_info, 0, flags); + if (chip_id(adap) > CHELSIO_T6) { + fatal |= t4_handle_intr(adap, &mps_tx_intr_info2, 0, flags); + fatal |= t4_handle_intr(adap, &mps_tx_intr_info3, 0, flags); + fatal |= t4_handle_intr(adap, &mps_tx_intr_info4, 0, flags); + fatal |= t4_handle_intr(adap, &t7_mps_trc_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &t7_mps_trc_intr_info2, 0, flags); + } else + fatal |= t4_handle_intr(adap, &mps_trc_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &mps_stat_sram_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &mps_stat_tx_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &mps_stat_rx_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &mps_cls_intr_info, 0, flags); + if (chip_id(adap) > CHELSIO_T4) + fatal |= t4_handle_intr(adap, &mps_stat_sram1_intr_info, 0, flags); t4_write_reg(adap, A_MPS_INT_CAUSE, is_t4(adap) ? 0 : 0xffffffff); t4_read_reg(adap, A_MPS_INT_CAUSE); /* flush */ @@ -5730,7 +6088,7 @@ static bool mps_intr_handler(struct adapter *adap, int arg, bool verbose) /* * EDC/MC interrupt handler. */ -static bool mem_intr_handler(struct adapter *adap, int idx, bool verbose) +static bool mem_intr_handler(struct adapter *adap, int idx, int flags) { static const char name[4][5] = { "EDC0", "EDC1", "MC0", "MC1" }; unsigned int count_reg, v; @@ -5740,61 +6098,106 @@ static bool mem_intr_handler(struct adapter *adap, int idx, bool verbose) { F_PERR_INT_CAUSE, "FIFO parity error" }, { 0 } }; + char rname[32]; struct intr_info ii = { + .name = &rname[0], .fatal = F_PERR_INT_CAUSE | F_ECC_UE_INT_CAUSE, .details = mem_intr_details, .flags = 0, .actions = NULL, }; - bool fatal; + bool fatal = false; + int i = 0; switch (idx) { + case MEM_EDC1: i = 1; + /* fall through */ case MEM_EDC0: - ii.name = "EDC0_INT_CAUSE"; - ii.cause_reg = EDC_REG(A_EDC_INT_CAUSE, 0); - ii.enable_reg = EDC_REG(A_EDC_INT_ENABLE, 0); - count_reg = EDC_REG(A_EDC_ECC_STATUS, 0); - break; - case MEM_EDC1: - ii.name = "EDC1_INT_CAUSE"; - ii.cause_reg = EDC_REG(A_EDC_INT_CAUSE, 1); - ii.enable_reg = EDC_REG(A_EDC_INT_ENABLE, 1); - count_reg = EDC_REG(A_EDC_ECC_STATUS, 1); + snprintf(rname, sizeof(rname), "EDC%u_INT_CAUSE", i); + if (is_t4(adap)) { + ii.cause_reg = EDC_REG(A_EDC_INT_CAUSE, i); + ii.enable_reg = EDC_REG(A_EDC_INT_ENABLE, i); + count_reg = EDC_REG(A_EDC_ECC_STATUS, i); + } else { + ii.cause_reg = EDC_T5_REG(A_EDC_H_INT_CAUSE, i); + ii.enable_reg = EDC_T5_REG(A_EDC_H_INT_ENABLE, i); + count_reg = EDC_T5_REG(A_EDC_H_ECC_STATUS, i); + } + fatal |= t4_handle_intr(adap, &ii, 0, flags); + if (chip_id(adap) > CHELSIO_T6) { + snprintf(rname, sizeof(rname), "EDC%u_PAR_CAUSE", i); + ii.cause_reg = EDC_T5_REG(A_EDC_H_PAR_CAUSE, i); + ii.enable_reg = EDC_T5_REG(A_EDC_H_PAR_ENABLE, i); + ii.fatal = 0xffffffff; + ii.details = NULL; + ii.flags = IHF_FATAL_IFF_ENABLED; + fatal |= t4_handle_intr(adap, &ii, 0, flags); + } break; + case MEM_MC1: + if (is_t4(adap) || is_t6(adap)) + return (false); + i = 1; + /* fall through */ case MEM_MC0: - ii.name = "MC0_INT_CAUSE"; + snprintf(rname, sizeof(rname), "MC%u_INT_CAUSE", i); if (is_t4(adap)) { ii.cause_reg = A_MC_INT_CAUSE; ii.enable_reg = A_MC_INT_ENABLE; count_reg = A_MC_ECC_STATUS; + } else if (chip_id(adap) < CHELSIO_T7) { + ii.cause_reg = MC_REG(A_MC_P_INT_CAUSE, i); + ii.enable_reg = MC_REG(A_MC_P_INT_ENABLE, i); + count_reg = MC_REG(A_MC_P_ECC_STATUS, i); } else { - ii.cause_reg = A_MC_P_INT_CAUSE; - ii.enable_reg = A_MC_P_INT_ENABLE; - count_reg = A_MC_P_ECC_STATUS; + ii.cause_reg = MC_T7_REG(A_T7_MC_P_INT_CAUSE, i); + ii.enable_reg = MC_T7_REG(A_T7_MC_P_INT_ENABLE, i); + count_reg = MC_T7_REG(A_T7_MC_P_ECC_STATUS, i); + } + fatal |= t4_handle_intr(adap, &ii, 0, flags); + + snprintf(rname, sizeof(rname), "MC%u_PAR_CAUSE", i); + if (is_t4(adap)) { + ii.cause_reg = A_MC_PAR_CAUSE; + ii.enable_reg = A_MC_PAR_ENABLE; + } else if (chip_id(adap) < CHELSIO_T7) { + ii.cause_reg = MC_REG(A_MC_P_PAR_CAUSE, i); + ii.enable_reg = MC_REG(A_MC_P_PAR_ENABLE, i); + } else { + ii.cause_reg = MC_T7_REG(A_T7_MC_P_PAR_CAUSE, i); + ii.enable_reg = MC_T7_REG(A_T7_MC_P_PAR_ENABLE, i); + } + ii.fatal = 0xffffffff; + ii.details = NULL; + ii.flags = IHF_FATAL_IFF_ENABLED; + fatal |= t4_handle_intr(adap, &ii, 0, flags); + + if (chip_id(adap) > CHELSIO_T6) { + snprintf(rname, sizeof(rname), "MC%u_DDRCTL_INT_CAUSE", i); + ii.cause_reg = MC_T7_REG(A_MC_P_DDRCTL_INT_CAUSE, i); + ii.enable_reg = MC_T7_REG(A_MC_P_DDRCTL_INT_ENABLE, i); + fatal |= t4_handle_intr(adap, &ii, 0, flags); + + snprintf(rname, sizeof(rname), "MC%u_ECC_UE_INT_CAUSE", i); + ii.cause_reg = MC_T7_REG(A_MC_P_ECC_UE_INT_CAUSE, i); + ii.enable_reg = MC_T7_REG(A_MC_P_ECC_UE_INT_ENABLE, i); + fatal |= t4_handle_intr(adap, &ii, 0, flags); } - break; - case MEM_MC1: - ii.name = "MC1_INT_CAUSE"; - ii.cause_reg = MC_REG(A_MC_P_INT_CAUSE, 1); - ii.enable_reg = MC_REG(A_MC_P_INT_ENABLE, 1); - count_reg = MC_REG(A_MC_P_ECC_STATUS, 1); break; } - fatal = t4_handle_intr(adap, &ii, 0, verbose); - v = t4_read_reg(adap, count_reg); if (v != 0) { - if (G_ECC_UECNT(v) != 0) { + if (G_ECC_UECNT(v) != 0 && !(flags & IHF_NO_SHOW)) { CH_ALERT(adap, - "%s: %u uncorrectable ECC data error(s)\n", + " %s: %u uncorrectable ECC data error(s)\n", name[idx], G_ECC_UECNT(v)); } - if (G_ECC_CECNT(v) != 0) { + if (G_ECC_CECNT(v) != 0 && !(flags & IHF_NO_SHOW)) { if (idx <= MEM_EDC1) t4_edc_err_read(adap, idx); CH_WARN_RATELIMIT(adap, - "%s: %u correctable ECC data error(s)\n", + " %s: %u correctable ECC data error(s)\n", name[idx], G_ECC_CECNT(v)); } t4_write_reg(adap, count_reg, 0xffffffff); @@ -5803,14 +6206,16 @@ static bool mem_intr_handler(struct adapter *adap, int idx, bool verbose) return (fatal); } -static bool ma_wrap_status(struct adapter *adap, int arg, bool verbose) +static bool ma_wrap_status(struct adapter *adap, int arg, int flags) { u32 v; v = t4_read_reg(adap, A_MA_INT_WRAP_STATUS); - CH_ALERT(adap, - "MA address wrap-around error by client %u to address %#x\n", - G_MEM_WRAP_CLIENT_NUM(v), G_MEM_WRAP_ADDRESS(v) << 4); + if (!(flags & IHF_NO_SHOW)) { + CH_ALERT(adap, + " MA address wrap-around by client %u to address %#x\n", + G_MEM_WRAP_CLIENT_NUM(v), G_MEM_WRAP_ADDRESS(v) << 4); + } t4_write_reg(adap, A_MA_INT_WRAP_STATUS, v); return (false); @@ -5820,7 +6225,7 @@ static bool ma_wrap_status(struct adapter *adap, int arg, bool verbose) /* * MA interrupt handler. */ -static bool ma_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool ma_intr_handler(struct adapter *adap, int arg, int flags) { static const struct intr_action ma_intr_actions[] = { { F_MEM_WRAP_INT_CAUSE, 0, ma_wrap_status }, @@ -5831,7 +6236,7 @@ static bool ma_intr_handler(struct adapter *adap, int arg, bool verbose) .cause_reg = A_MA_INT_CAUSE, .enable_reg = A_MA_INT_ENABLE, .fatal = F_MEM_PERR_INT_CAUSE | F_MEM_TO_INT_CAUSE, - .flags = NONFATAL_IF_DISABLED, + .flags = IHF_FATAL_IFF_ENABLED, .details = NULL, .actions = ma_intr_actions, }; @@ -5856,10 +6261,10 @@ static bool ma_intr_handler(struct adapter *adap, int arg, bool verbose) bool fatal; fatal = false; - fatal |= t4_handle_intr(adap, &ma_intr_info, 0, verbose); - fatal |= t4_handle_intr(adap, &ma_perr_status1, 0, verbose); + fatal |= t4_handle_intr(adap, &ma_intr_info, 0, flags); + fatal |= t4_handle_intr(adap, &ma_perr_status1, 0, flags); if (chip_id(adap) > CHELSIO_T4) - fatal |= t4_handle_intr(adap, &ma_perr_status2, 0, verbose); + fatal |= t4_handle_intr(adap, &ma_perr_status2, 0, flags); return (fatal); } @@ -5867,58 +6272,115 @@ static bool ma_intr_handler(struct adapter *adap, int arg, bool verbose) /* * SMB interrupt handler. */ -static bool smb_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool smb_intr_handler(struct adapter *adap, int arg, int flags) { - static const struct intr_details smb_intr_details[] = { + static const struct intr_details smb_int_cause_fields[] = { { F_MSTTXFIFOPARINT, "SMB master Tx FIFO parity error" }, { F_MSTRXFIFOPARINT, "SMB master Rx FIFO parity error" }, { F_SLVFIFOPARINT, "SMB slave FIFO parity error" }, { 0 } }; - static const struct intr_info smb_intr_info = { + static const struct intr_info smb_int_cause = { .name = "SMB_INT_CAUSE", .cause_reg = A_SMB_INT_CAUSE, .enable_reg = A_SMB_INT_ENABLE, .fatal = F_SLVFIFOPARINT | F_MSTRXFIFOPARINT | F_MSTTXFIFOPARINT, .flags = 0, - .details = smb_intr_details, + .details = smb_int_cause_fields, .actions = NULL, }; - - return (t4_handle_intr(adap, &smb_intr_info, 0, verbose)); + return (t4_handle_intr(adap, &smb_int_cause, 0, flags)); } /* * NC-SI interrupt handler. */ -static bool ncsi_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool ncsi_intr_handler(struct adapter *adap, int arg, int flags) { - static const struct intr_details ncsi_intr_details[] = { + static const struct intr_details ncsi_int_cause_fields[] = { { F_CIM_DM_PRTY_ERR, "NC-SI CIM parity error" }, { F_MPS_DM_PRTY_ERR, "NC-SI MPS parity error" }, { F_TXFIFO_PRTY_ERR, "NC-SI Tx FIFO parity error" }, { F_RXFIFO_PRTY_ERR, "NC-SI Rx FIFO parity error" }, { 0 } }; - static const struct intr_info ncsi_intr_info = { + static const struct intr_info ncsi_int_cause = { .name = "NCSI_INT_CAUSE", .cause_reg = A_NCSI_INT_CAUSE, .enable_reg = A_NCSI_INT_ENABLE, .fatal = F_RXFIFO_PRTY_ERR | F_TXFIFO_PRTY_ERR | F_MPS_DM_PRTY_ERR | F_CIM_DM_PRTY_ERR, .flags = 0, - .details = ncsi_intr_details, + .details = ncsi_int_cause_fields, + .actions = NULL, + }; + static const struct intr_info ncsi_xgmac0_int_cause = { + .name = "NCSI_XGMAC0_INT_CAUSE", + .cause_reg = A_NCSI_XGMAC0_INT_CAUSE, + .enable_reg = A_NCSI_XGMAC0_INT_ENABLE, + .fatal = 0, + .flags = 0, + .details = NULL, .actions = NULL, }; + bool fatal = false; - return (t4_handle_intr(adap, &ncsi_intr_info, 0, verbose)); + fatal |= t4_handle_intr(adap, &ncsi_int_cause, 0, flags); + if (chip_id(adap) > CHELSIO_T6) + fatal |= t4_handle_intr(adap, &ncsi_xgmac0_int_cause, 0, flags); + return (fatal); } /* * MAC interrupt handler. */ -static bool mac_intr_handler(struct adapter *adap, int port, bool verbose) +static bool mac_intr_handler(struct adapter *adap, int port, int flags) { + static const struct intr_info mac_int_cause_cmn = { + .name = "MAC_INT_CAUSE_CMN", + .cause_reg = A_MAC_INT_CAUSE_CMN, + .enable_reg = A_MAC_INT_EN_CMN, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mac_perr_cause_mtip = { + .name = "MAC_PERR_INT_CAUSE_MTIP", + .cause_reg = A_MAC_PERR_INT_CAUSE_MTIP, + .enable_reg = A_MAC_PERR_INT_EN_MTIP, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED | IHF_IGNORE_IF_DISABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mac_cerr_cause_mtip = { + .name = "MAC_CERR_INT_CAUSE_MTIP", + .cause_reg = A_MAC_CERR_INT_CAUSE_MTIP, + .enable_reg = A_MAC_CERR_INT_EN_MTIP, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mac_ios_int_cause_quad0 = { + .name = "MAC_IOS_INTR_CAUSE_QUAD0", + .cause_reg = A_MAC_IOS_INTR_CAUSE_QUAD0, + .enable_reg = A_MAC_IOS_INTR_EN_QUAD0, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info mac_ios_int_cause_quad1 = { + .name = "MAC_IOS_INTR_CAUSE_QUAD1", + .cause_reg = A_MAC_IOS_INTR_CAUSE_QUAD1, + .enable_reg = A_MAC_IOS_INTR_EN_QUAD1, + .fatal = 0, + .flags = 0, + .details = NULL, + .actions = NULL, + }; static const struct intr_details mac_intr_details[] = { { F_TXFIFO_PRTY_ERR, "MAC Tx FIFO parity error" }, { F_RXFIFO_PRTY_ERR, "MAC Rx FIFO parity error" }, @@ -5928,6 +6390,9 @@ static bool mac_intr_handler(struct adapter *adap, int port, bool verbose) struct intr_info ii; bool fatal = false; + if (port > 1 && is_t6(adap)) + return (false); + if (is_t4(adap)) { snprintf(name, sizeof(name), "XGMAC_PORT%u_INT_CAUSE", port); ii.name = &name[0]; @@ -5947,66 +6412,79 @@ static bool mac_intr_handler(struct adapter *adap, int port, bool verbose) ii.details = mac_intr_details; ii.actions = NULL; } else { - snprintf(name, sizeof(name), "T7_MAC_PORT%u_INT_CAUSE", port); + snprintf(name, sizeof(name), "MAC_PORT%u_INT_CAUSE", port); ii.name = &name[0]; ii.cause_reg = T7_PORT_REG(port, A_T7_MAC_PORT_INT_CAUSE); ii.enable_reg = T7_PORT_REG(port, A_T7_MAC_PORT_INT_EN); - ii.fatal = F_TXFIFO_PRTY_ERR | F_RXFIFO_PRTY_ERR; - ii.flags = 0; - ii.details = mac_intr_details; + ii.fatal = 0xffffffff; + ii.flags = IHF_FATAL_IFF_ENABLED; + ii.details = NULL; ii.actions = NULL; } - fatal |= t4_handle_intr(adap, &ii, 0, verbose); + fatal |= t4_handle_intr(adap, &ii, 0, flags); + if (is_t4(adap)) + return (fatal); + MPASS(chip_id(adap) >= CHELSIO_T5); + snprintf(name, sizeof(name), "MAC_PORT%u_PERR_INT_CAUSE", port); if (chip_id(adap) > CHELSIO_T6) { - snprintf(name, sizeof(name), "T7_MAC_PORT%u_PERR_INT_CAUSE", port); ii.name = &name[0]; ii.cause_reg = T7_PORT_REG(port, A_T7_MAC_PORT_PERR_INT_CAUSE); ii.enable_reg = T7_PORT_REG(port, A_T7_MAC_PORT_PERR_INT_EN); - ii.fatal = 0; - ii.flags = 0; + ii.fatal = 0xffffffff; + ii.flags = IHF_FATAL_IFF_ENABLED; ii.details = NULL; ii.actions = NULL; - fatal |= t4_handle_intr(adap, &ii, 0, verbose); - } else if (chip_id(adap) >= CHELSIO_T5) { - snprintf(name, sizeof(name), "MAC_PORT%u_PERR_INT_CAUSE", port); + } else { ii.name = &name[0]; ii.cause_reg = T5_PORT_REG(port, A_MAC_PORT_PERR_INT_CAUSE); ii.enable_reg = T5_PORT_REG(port, A_MAC_PORT_PERR_INT_EN); - ii.fatal = 0; - ii.flags = 0; + ii.fatal = 0xffffffff; + ii.flags = IHF_FATAL_IFF_ENABLED; ii.details = NULL; ii.actions = NULL; - fatal |= t4_handle_intr(adap, &ii, 0, verbose); } + fatal |= t4_handle_intr(adap, &ii, 0, flags); + if (is_t5(adap)) + return (fatal); + MPASS(chip_id(adap) >= CHELSIO_T6); + snprintf(name, sizeof(name), "MAC_PORT%u_PERR_INT_CAUSE_100G", port); if (chip_id(adap) > CHELSIO_T6) { - snprintf(name, sizeof(name), "T7_MAC_PORT%u_PERR_INT_CAUSE_100G", port); ii.name = &name[0]; ii.cause_reg = T7_PORT_REG(port, A_T7_MAC_PORT_PERR_INT_CAUSE_100G); ii.enable_reg = T7_PORT_REG(port, A_T7_MAC_PORT_PERR_INT_EN_100G); - ii.fatal = 0; - ii.flags = 0; + ii.fatal = 0xffffffff; + ii.flags = IHF_FATAL_IFF_ENABLED; ii.details = NULL; ii.actions = NULL; - fatal |= t4_handle_intr(adap, &ii, 0, verbose); - } else if (is_t6(adap)) { - snprintf(name, sizeof(name), "MAC_PORT%u_PERR_INT_CAUSE_100G", port); + } else { ii.name = &name[0]; ii.cause_reg = T5_PORT_REG(port, A_MAC_PORT_PERR_INT_CAUSE_100G); ii.enable_reg = T5_PORT_REG(port, A_MAC_PORT_PERR_INT_EN_100G); - ii.fatal = 0; - ii.flags = 0; + ii.fatal = 0xffffffff; + ii.flags = IHF_FATAL_IFF_ENABLED; ii.details = NULL; ii.actions = NULL; - fatal |= t4_handle_intr(adap, &ii, 0, verbose); } + fatal |= t4_handle_intr(adap, &ii, 0, flags); + if (is_t6(adap)) + return (fatal); + + MPASS(chip_id(adap) >= CHELSIO_T7); + fatal |= t4_handle_intr(adap, &mac_int_cause_cmn, 0, flags); + fatal |= t4_handle_intr(adap, &mac_perr_cause_mtip, 0, flags); + fatal |= t4_handle_intr(adap, &mac_cerr_cause_mtip, 0, flags); + fatal |= t4_handle_intr(adap, &mac_ios_int_cause_quad0, 0, flags); + fatal |= t4_handle_intr(adap, &mac_ios_int_cause_quad1, 0, flags); return (fatal); } -static bool pl_timeout_status(struct adapter *adap, int arg, bool verbose) +static bool pl_timeout_status(struct adapter *adap, int arg, int flags) { + if (flags & IHF_NO_SHOW) + return (false); CH_ALERT(adap, " PL_TIMEOUT_STATUS 0x%08x 0x%08x\n", t4_read_reg(adap, A_PL_TIMEOUT_STATUS0), @@ -6015,13 +6493,9 @@ static bool pl_timeout_status(struct adapter *adap, int arg, bool verbose) return (false); } -static bool plpl_intr_handler(struct adapter *adap, int arg, bool verbose) +static bool plpl_intr_handler(struct adapter *adap, int arg, int flags) { - static const struct intr_action plpl_intr_actions[] = { - { F_TIMEOUT, 0, pl_timeout_status }, - { 0 }, - }; - static const struct intr_details plpl_intr_details[] = { + static const struct intr_details plpl_int_cause_fields[] = { { F_PL_BUSPERR, "Bus parity error" }, { F_FATALPERR, "Fatal parity error" }, { F_INVALIDACCESS, "Global reserved memory access" }, @@ -6030,31 +6504,397 @@ static bool plpl_intr_handler(struct adapter *adap, int arg, bool verbose) { F_PERRVFID, "VFID_MAP parity error" }, { 0 } }; - static const struct intr_info plpl_intr_info = { + static const struct intr_action plpl_int_cause_actions[] = { + { F_TIMEOUT, -1, pl_timeout_status }, + { 0 }, + }; + static const struct intr_info plpl_int_cause = { .name = "PL_PL_INT_CAUSE", .cause_reg = A_PL_PL_INT_CAUSE, .enable_reg = A_PL_PL_INT_ENABLE, .fatal = F_FATALPERR | F_PERRVFID, - .flags = NONFATAL_IF_DISABLED, - .details = plpl_intr_details, - .actions = plpl_intr_actions, + .flags = IHF_FATAL_IFF_ENABLED | IHF_IGNORE_IF_DISABLED, + .details = plpl_int_cause_fields, + .actions = plpl_int_cause_actions, + }; + + return (t4_handle_intr(adap, &plpl_int_cause, 0, flags)); +} + +/* similar to t4_port_reg */ +static inline u32 +t7_tlstx_reg(u8 instance, u8 channel, u32 reg) +{ + MPASS(instance <= 1); + MPASS(channel < NUM_TLS_TX_CH_INSTANCES); + return (instance * (CRYPTO_1_BASE_ADDR - CRYPTO_0_BASE_ADDR) + + TLS_TX_CH_REG(reg, channel)); +} + +/* + * CRYPTO (aka TLS_TX) interrupt handler. + */ +static bool tlstx_intr_handler(struct adapter *adap, int idx, int flags) +{ + static const struct intr_details tlstx_int_cause_fields[] = { + { F_KEX_CERR, "KEX SRAM Correctable error" }, + { F_KEYLENERR, "IPsec Key length error" }, + { F_INTF1_PERR, "Input Interface1 parity error" }, + { F_INTF0_PERR, "Input Interface0 parity error" }, + { F_KEX_PERR, "KEX SRAM Parity error" }, + { 0 } + }; + struct intr_info ii = { + .fatal = F_KEX_PERR | F_INTF0_PERR | F_INTF1_PERR, + .flags = IHF_FATAL_IFF_ENABLED, + .details = tlstx_int_cause_fields, + .actions = NULL, + }; + char name[32]; + int ch; + bool fatal = false; + + for (ch = 0; ch < NUM_TLS_TX_CH_INSTANCES; ch++) { + snprintf(name, sizeof(name), "TLSTX%u_CH%u_INT_CAUSE", idx, ch); + ii.name = &name[0]; + ii.cause_reg = t7_tlstx_reg(idx, ch, A_TLS_TX_CH_INT_CAUSE); + ii.enable_reg = t7_tlstx_reg(idx, ch, A_TLS_TX_CH_INT_ENABLE); + fatal |= t4_handle_intr(adap, &ii, 0, flags); + } + + return (fatal); +} + +/* + * HMA interrupt handler. + */ +static bool hma_intr_handler(struct adapter *adap, int idx, int flags) +{ + static const struct intr_details hma_int_cause_fields[] = { + { F_GK_UF_INT_CAUSE, "Gatekeeper underflow" }, + { F_IDTF_INT_CAUSE, "Invalid descriptor fault" }, + { F_OTF_INT_CAUSE, "Offset translation fault" }, + { F_RTF_INT_CAUSE, "Region translation fault" }, + { F_PCIEMST_INT_CAUSE, "PCIe master access error" }, + { F_MAMST_INT_CAUSE, "MA master access error" }, + { 1, "FIFO parity error" }, + { 0 } + }; + static const struct intr_info hma_int_cause = { + .name = "HMA_INT_CAUSE", + .cause_reg = A_HMA_INT_CAUSE, + .enable_reg = A_HMA_INT_ENABLE, + .fatal = 7, + .flags = 0, + .details = hma_int_cause_fields, + .actions = NULL, + }; + + return (t4_handle_intr(adap, &hma_int_cause, 0, flags)); +} + +/* + * CRYPTO_KEY interrupt handler. + */ +static bool cryptokey_intr_handler(struct adapter *adap, int idx, int flags) +{ + static const struct intr_details cryptokey_int_cause_fields[] = { + { F_MA_FIFO_PERR, "MA arbiter FIFO parity error" }, + { F_MA_RSP_PERR, "MA response IF parity error" }, + { F_ING_CACHE_DATA_PERR, "Ingress key cache data parity error" }, + { F_ING_CACHE_TAG_PERR, "Ingress key cache tag parity error" }, + { F_LKP_KEY_REQ_PERR, "Ingress key req parity error" }, + { F_LKP_CLIP_TCAM_PERR, "Ingress LKP CLIP TCAM parity error" }, + { F_LKP_MAIN_TCAM_PERR, "Ingress LKP main TCAM parity error" }, + { F_EGR_KEY_REQ_PERR, "Egress key req or FIFO3 parity error" }, + { F_EGR_CACHE_DATA_PERR, "Egress key cache data parity error" }, + { F_EGR_CACHE_TAG_PERR, "Egress key cache tag parity error" }, + { F_CIM_PERR, "CIM interface parity error" }, + { F_MA_INV_RSP_TAG, "MA invalid response tag" }, + { F_ING_KEY_RANGE_ERR, "Ingress key range error" }, + { F_ING_MFIFO_OVFL, "Ingress MFIFO overflow" }, + { F_LKP_REQ_OVFL, "Ingress lookup FIFO overflow" }, + { F_EOK_WAIT_ERR, "EOK wait error" }, + { F_EGR_KEY_RANGE_ERR, "Egress key range error" }, + { F_EGR_MFIFO_OVFL, "Egress MFIFO overflow" }, + { F_SEQ_WRAP_HP_OVFL, "Sequence wrap (hi-pri)" }, + { F_SEQ_WRAP_LP_OVFL, "Sequence wrap (lo-pri)" }, + { F_EGR_SEQ_WRAP_HP, "Egress sequence wrap (hi-pri)" }, + { F_EGR_SEQ_WRAP_LP, "Egress sequence wrap (lo-pri)" }, + { 0 } + }; + static const struct intr_info cryptokey_int_cause = { + .name = "CRYPTO_KEY_INT_CAUSE", + .cause_reg = A_CRYPTO_KEY_INT_CAUSE, + .enable_reg = A_CRYPTO_KEY_INT_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = cryptokey_int_cause_fields, + .actions = NULL, + }; + + return (t4_handle_intr(adap, &cryptokey_int_cause, 0, flags)); +} + +/* + * GCACHE interrupt handler. + */ +static bool gcache_intr_handler(struct adapter *adap, int idx, int flags) +{ + static const struct intr_details gcache_int_cause_fields[] = { + { F_GC1_SRAM_RSP_DATAQ_PERR_INT_CAUSE, "GC1 SRAM rsp dataq perr" }, + { F_GC0_SRAM_RSP_DATAQ_PERR_INT_CAUSE, "GC0 SRAM rsp dataq perr" }, + { F_GC1_WQDATA_FIFO_PERR_INT_CAUSE, "GC1 wqdata FIFO perr" }, + { F_GC0_WQDATA_FIFO_PERR_INT_CAUSE, "GC0 wqdata FIFO perr" }, + { F_GC1_RDTAG_QUEUE_PERR_INT_CAUSE, "GC1 rdtag queue perr" }, + { F_GC0_RDTAG_QUEUE_PERR_INT_CAUSE, "GC0 rdtag queue perr" }, + { F_GC1_SRAM_RDTAG_QUEUE_PERR_INT_CAUSE, "GC1 SRAM rdtag queue perr" }, + { F_GC0_SRAM_RDTAG_QUEUE_PERR_INT_CAUSE, "GC0 SRAM rdtag queue perr" }, + { F_GC1_RSP_PERR_INT_CAUSE, "GC1 rsp perr" }, + { F_GC0_RSP_PERR_INT_CAUSE, "GC0 rsp perr" }, + { F_GC1_LRU_UERR_INT_CAUSE, "GC1 lru uerr" }, + { F_GC0_LRU_UERR_INT_CAUSE, "GC0 lru uerr" }, + { F_GC1_TAG_UERR_INT_CAUSE, "GC1 tag uerr" }, + { F_GC0_TAG_UERR_INT_CAUSE, "GC0 tag uerr" }, + { F_GC1_LRU_CERR_INT_CAUSE, "GC1 lru cerr" }, + { F_GC0_LRU_CERR_INT_CAUSE, "GC0 lru cerr" }, + { F_GC1_TAG_CERR_INT_CAUSE, "GC1 tag cerr" }, + { F_GC0_TAG_CERR_INT_CAUSE, "GC0 tag cerr" }, + { F_GC1_CE_INT_CAUSE, "GC1 correctable error" }, + { F_GC0_CE_INT_CAUSE, "GC0 correctable error" }, + { F_GC1_UE_INT_CAUSE, "GC1 uncorrectable error" }, + { F_GC0_UE_INT_CAUSE, "GC0 uncorrectable error" }, + { F_GC1_CMD_PAR_INT_CAUSE, "GC1 cmd perr" }, + { F_GC1_DATA_PAR_INT_CAUSE, "GC1 data perr" }, + { F_GC0_CMD_PAR_INT_CAUSE, "GC0 cmd perr" }, + { F_GC0_DATA_PAR_INT_CAUSE, "GC0 data perr" }, + { F_ILLADDRACCESS1_INT_CAUSE, "GC1 illegal address access" }, + { F_ILLADDRACCESS0_INT_CAUSE, "GC0 illegal address access" }, + { 0 } + }; + static const struct intr_info gcache_perr_cause = { + .name = "GCACHE_PAR_CAUSE", + .cause_reg = A_GCACHE_PAR_CAUSE, + .enable_reg = A_GCACHE_PAR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info gcache_int_cause = { + .name = "GCACHE_INT_CAUSE", + .cause_reg = A_GCACHE_INT_CAUSE, + .enable_reg = A_GCACHE_INT_ENABLE, + .fatal = 0, + .flags = 0, + .details = gcache_int_cause_fields, + .actions = NULL, + }; + bool fatal = false; + + fatal |= t4_handle_intr(adap, &gcache_int_cause, 0, flags); + fatal |= t4_handle_intr(adap, &gcache_perr_cause, 0, flags); + + return (fatal); +} + +/* + * ARM interrupt handler. + */ +static bool arm_intr_handler(struct adapter *adap, int idx, int flags) +{ + static const struct intr_info arm_perr_cause0 = { + .name = "ARM_PERR_INT_CAUSE0", + .cause_reg = A_ARM_PERR_INT_CAUSE0, + .enable_reg = A_ARM_PERR_INT_ENB0, + .fatal = 0xffffffff, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info arm_perr_cause1 = { + .name = "ARM_PERR_INT_CAUSE1", + .cause_reg = A_ARM_PERR_INT_CAUSE1, + .enable_reg = A_ARM_PERR_INT_ENB1, + .fatal = 0xffffffff, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info arm_perr_cause2 = { + .name = "ARM_PERR_INT_CAUSE2", + .cause_reg = A_ARM_PERR_INT_CAUSE2, + .enable_reg = A_ARM_PERR_INT_ENB2, + .fatal = 0xffffffff, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info arm_cerr_cause0 = { + .name = "ARM_CERR_INT_CAUSE", + .cause_reg = A_ARM_CERR_INT_CAUSE0, + .enable_reg = A_ARM_CERR_INT_ENB0, + .fatal = 0, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, }; + static const struct intr_info arm_err_cause0 = { + .name = "ARM_ERR_INT_CAUSE", + .cause_reg = A_ARM_ERR_INT_CAUSE0, + .enable_reg = A_ARM_ERR_INT_ENB0, + .fatal = 0, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info arm_periph_cause = { + .name = "ARM_PERIPHERAL_INT_CAUSE", + .cause_reg = A_ARM_PERIPHERAL_INT_CAUSE, + .enable_reg = A_ARM_PERIPHERAL_INT_ENB, + .fatal = 0, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + static const struct intr_info arm_nvme_db_emu_cause = { + .name = "ARM_NVME_DB_EMU_INT_CAUSE", + .cause_reg = A_ARM_NVME_DB_EMU_INT_CAUSE, + .enable_reg = A_ARM_NVME_DB_EMU_INT_ENABLE, + .fatal = 0, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = NULL, + .actions = NULL, + }; + bool fatal = false; + + fatal |= t4_handle_intr(adap, &arm_perr_cause0, 0, flags); + fatal |= t4_handle_intr(adap, &arm_perr_cause1, 0, flags); + fatal |= t4_handle_intr(adap, &arm_perr_cause2, 0, flags); + fatal |= t4_handle_intr(adap, &arm_cerr_cause0, 0, flags); + fatal |= t4_handle_intr(adap, &arm_err_cause0, 0, flags); + fatal |= t4_handle_intr(adap, &arm_periph_cause, 0, flags); + fatal |= t4_handle_intr(adap, &arm_nvme_db_emu_cause, 0, flags); + + return (fatal); +} + +static inline uint32_t +get_perr_ucause(struct adapter *sc, const struct intr_info *ii) +{ + uint32_t cause; - return (t4_handle_intr(adap, &plpl_intr_info, 0, verbose)); + cause = t4_read_reg(sc, ii->cause_reg); + if (ii->flags & IHF_IGNORE_IF_DISABLED) + cause &= t4_read_reg(sc, ii->enable_reg); + return (cause); +} + +static uint32_t +t4_perr_to_ic(struct adapter *adap, uint32_t perr) +{ + uint32_t mask; + + if (adap->chip_params->nchan > 2) + mask = F_MAC0 | F_MAC1 | F_MAC2 | F_MAC3; + else + mask = F_MAC0 | F_MAC1; + return (perr & mask ? perr | mask : perr); +} + +static uint32_t +t7_perr_to_ic1(uint32_t perr) +{ + uint32_t cause = 0; + + if (perr & F_T7_PL_PERR_ULP_TX) + cause |= F_T7_ULP_TX; + if (perr & F_T7_PL_PERR_SGE) + cause |= F_T7_SGE; + if (perr & F_T7_PL_PERR_HMA) + cause |= F_T7_HMA; + if (perr & F_T7_PL_PERR_CPL_SWITCH) + cause |= F_T7_CPL_SWITCH; + if (perr & F_T7_PL_PERR_ULP_RX) + cause |= F_T7_ULP_RX; + if (perr & F_T7_PL_PERR_PM_RX) + cause |= F_T7_PM_RX; + if (perr & F_T7_PL_PERR_PM_TX) + cause |= F_T7_PM_TX; + if (perr & F_T7_PL_PERR_MA) + cause |= F_T7_MA; + if (perr & F_T7_PL_PERR_TP) + cause |= F_T7_TP; + if (perr & F_T7_PL_PERR_LE) + cause |= F_T7_LE; + if (perr & F_T7_PL_PERR_EDC1) + cause |= F_T7_EDC1; + if (perr & F_T7_PL_PERR_EDC0) + cause |= F_T7_EDC0; + if (perr & F_T7_PL_PERR_MC1) + cause |= F_T7_MC1; + if (perr & F_T7_PL_PERR_MC0) + cause |= F_T7_MC0; + if (perr & F_T7_PL_PERR_PCIE) + cause |= F_T7_PCIE; + if (perr & F_T7_PL_PERR_UART) + cause |= F_T7_UART; + if (perr & F_T7_PL_PERR_PMU) + cause |= F_PMU; + if (perr & F_T7_PL_PERR_MAC) + cause |= F_MAC0 | F_MAC1 | F_MAC2 | F_MAC3; + if (perr & F_T7_PL_PERR_SMB) + cause |= F_SMB; + if (perr & F_T7_PL_PERR_SF) + cause |= F_SF; + if (perr & F_T7_PL_PERR_PL) + cause |= F_PL; + if (perr & F_T7_PL_PERR_NCSI) + cause |= F_NCSI; + if (perr & F_T7_PL_PERR_MPS) + cause |= F_MPS; + if (perr & F_T7_PL_PERR_MI) + cause |= F_MI; + if (perr & F_T7_PL_PERR_DBG) + cause |= F_DBG; + if (perr & F_T7_PL_PERR_I2CM) + cause |= F_I2CM; + if (perr & F_T7_PL_PERR_CIM) + cause |= F_CIM; + + return (cause); +} + +static uint32_t +t7_perr_to_ic2(uint32_t perr) +{ + uint32_t cause = 0; + + if (perr & F_T7_PL_PERR_CRYPTO_KEY) + cause |= F_CRYPTO_KEY; + if (perr & F_T7_PL_PERR_CRYPTO1) + cause |= F_CRYPTO1; + if (perr & F_T7_PL_PERR_CRYPTO0) + cause |= F_CRYPTO0; + if (perr & F_T7_PL_PERR_GCACHE) + cause |= F_GCACHE; + if (perr & F_T7_PL_PERR_ARM) + cause |= F_ARM; + + return (cause); } /** * t4_slow_intr_handler - control path interrupt handler * @adap: the adapter - * @verbose: increased verbosity, for debug * * T4 interrupt handler for non-data global interrupt events, e.g., errors. * The designation 'slow' is because it involves register reads, while * data interrupts typically don't involve any MMIOs. */ -bool t4_slow_intr_handler(struct adapter *adap, bool verbose) +bool t4_slow_intr_handler(struct adapter *adap, int flags) { - static const struct intr_details pl_intr_details[] = { + static const struct intr_details pl_int_cause_fields[] = { { F_MC1, "MC1" }, { F_UART, "UART" }, { F_ULP_TX, "ULP TX" }, @@ -6087,10 +6927,56 @@ bool t4_slow_intr_handler(struct adapter *adap, bool verbose) { F_CIM, "CIM" }, { 0 } }; - static const struct intr_details t7_pl_intr_details[] = { - { F_T7_MC1, "MC1" }, + static const struct intr_action pl_int_cause_actions[] = { + { F_ULP_TX, -1, ulptx_intr_handler }, + { F_SGE, -1, sge_intr_handler }, + { F_CPL_SWITCH, -1, cplsw_intr_handler }, + { F_ULP_RX, -1, ulprx_intr_handler }, + { F_PM_RX, -1, pmtx_intr_handler }, + { F_PM_TX, -1, pmtx_intr_handler }, + { F_MA, -1, ma_intr_handler }, + { F_TP, -1, tp_intr_handler }, + { F_LE, -1, le_intr_handler }, + { F_EDC0, MEM_EDC0, mem_intr_handler }, + { F_EDC1, MEM_EDC1, mem_intr_handler }, + { F_MC0, MEM_MC0, mem_intr_handler }, + { F_MC1, MEM_MC1, mem_intr_handler }, + { F_PCIE, -1, pcie_intr_handler }, + { F_MAC0, 0, mac_intr_handler }, + { F_MAC1, 1, mac_intr_handler }, + { F_MAC2, 2, mac_intr_handler }, + { F_MAC3, 3, mac_intr_handler }, + { F_SMB, -1, smb_intr_handler }, + { F_PL, -1, plpl_intr_handler }, + { F_NCSI, -1, ncsi_intr_handler }, + { F_MPS, -1, mps_intr_handler }, + { F_CIM, -1, cim_intr_handler }, + { 0 } + }; + static const struct intr_info pl_int_cause = { + .name = "PL_INT_CAUSE", + .cause_reg = A_PL_INT_CAUSE, + .enable_reg = A_PL_INT_ENABLE, + .fatal = 0, + .flags = IHF_IGNORE_IF_DISABLED, + .details = pl_int_cause_fields, + .actions = pl_int_cause_actions, + }; + static const struct intr_info pl_perr_cause = { + .name = "PL_PERR_CAUSE", + .cause_reg = A_PL_PERR_CAUSE, + .enable_reg = A_PL_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = pl_int_cause_fields, + .actions = NULL, + }; + static const struct intr_details t7_pl_int_cause_fields[] = { + { F_T7_FLR, "FLR" }, + { F_T7_SW_CIM, "SW CIM" }, { F_T7_ULP_TX, "ULP TX" }, { F_T7_SGE, "SGE" }, + { F_T7_HMA, "HMA" }, { F_T7_CPL_SWITCH, "CPL Switch" }, { F_T7_ULP_RX, "ULP RX" }, { F_T7_PM_RX, "PM RX" }, @@ -6100,117 +6986,165 @@ bool t4_slow_intr_handler(struct adapter *adap, bool verbose) { F_T7_LE, "LE" }, { F_T7_EDC1, "EDC1" }, { F_T7_EDC0, "EDC0" }, + { F_T7_MC1, "MC1" }, { F_T7_MC0, "MC0" }, { F_T7_PCIE, "PCIE" }, + { F_T7_UART, "UART" }, + { F_PMU, "PMU" }, { F_MAC3, "MAC3" }, { F_MAC2, "MAC2" }, { F_MAC1, "MAC1" }, { F_MAC0, "MAC0" }, { F_SMB, "SMB" }, + { F_SF, "SF" }, { F_PL, "PL" }, { F_NCSI, "NC-SI" }, { F_MPS, "MPS" }, + { F_MI, "MI" }, { F_DBG, "DBG" }, { F_I2CM, "I2CM" }, - { F_MI, "MI" }, { F_CIM, "CIM" }, { 0 } }; - struct intr_info pl_perr_cause = { - .name = "PL_PERR_CAUSE", - .cause_reg = A_PL_PERR_CAUSE, - .enable_reg = A_PL_PERR_ENABLE, - .fatal = 0xffffffff, - .flags = NONFATAL_IF_DISABLED, - .details = NULL, - .actions = NULL, - }; - static const struct intr_action pl_intr_action[] = { - { F_MC1, MEM_MC1, mem_intr_handler }, - { F_ULP_TX, -1, ulptx_intr_handler }, - { F_SGE, -1, sge_intr_handler }, - { F_CPL_SWITCH, -1, cplsw_intr_handler }, - { F_ULP_RX, -1, ulprx_intr_handler }, - { F_PM_RX, -1, pmrx_intr_handler}, - { F_PM_TX, -1, pmtx_intr_handler}, - { F_MA, -1, ma_intr_handler }, - { F_TP, -1, tp_intr_handler }, - { F_LE, -1, le_intr_handler }, - { F_EDC1, MEM_EDC1, mem_intr_handler }, - { F_EDC0, MEM_EDC0, mem_intr_handler }, - { F_MC0, MEM_MC0, mem_intr_handler }, - { F_PCIE, -1, pcie_intr_handler }, - { F_MAC3, 3, mac_intr_handler}, - { F_MAC2, 2, mac_intr_handler}, - { F_MAC1, 1, mac_intr_handler}, - { F_MAC0, 0, mac_intr_handler}, - { F_SMB, -1, smb_intr_handler}, - { F_PL, -1, plpl_intr_handler }, - { F_NCSI, -1, ncsi_intr_handler}, - { F_MPS, -1, mps_intr_handler }, - { F_CIM, -1, cim_intr_handler }, - { 0 } - }; - static const struct intr_action t7_pl_intr_action[] = { + static const struct intr_action t7_pl_int_cause_actions[] = { { F_T7_ULP_TX, -1, ulptx_intr_handler }, { F_T7_SGE, -1, sge_intr_handler }, + { F_T7_HMA, -1, hma_intr_handler }, { F_T7_CPL_SWITCH, -1, cplsw_intr_handler }, { F_T7_ULP_RX, -1, ulprx_intr_handler }, - { F_T7_PM_RX, -1, pmrx_intr_handler}, - { F_T7_PM_TX, -1, pmtx_intr_handler}, + { F_T7_PM_RX, -1, pmrx_intr_handler }, + { F_T7_PM_TX, -1, pmtx_intr_handler }, { F_T7_MA, -1, ma_intr_handler }, { F_T7_TP, -1, tp_intr_handler }, { F_T7_LE, -1, le_intr_handler }, - { F_T7_EDC1, MEM_EDC1, mem_intr_handler }, { F_T7_EDC0, MEM_EDC0, mem_intr_handler }, - { F_T7_MC1, MEM_MC1, mem_intr_handler }, + { F_T7_EDC1, MEM_EDC1, mem_intr_handler }, { F_T7_MC0, MEM_MC0, mem_intr_handler }, + { F_T7_MC1, MEM_MC1, mem_intr_handler }, { F_T7_PCIE, -1, pcie_intr_handler }, - { F_MAC3, 3, mac_intr_handler}, - { F_MAC2, 2, mac_intr_handler}, - { F_MAC1, 1, mac_intr_handler}, - { F_MAC0, 0, mac_intr_handler}, - { F_SMB, -1, smb_intr_handler}, + { F_MAC0, 0, mac_intr_handler }, + { F_MAC1, 1, mac_intr_handler }, + { F_MAC2, 2, mac_intr_handler }, + { F_MAC3, 3, mac_intr_handler }, + { F_SMB, -1, smb_intr_handler }, { F_PL, -1, plpl_intr_handler }, - { F_NCSI, -1, ncsi_intr_handler}, + { F_NCSI, -1, ncsi_intr_handler }, { F_MPS, -1, mps_intr_handler }, { F_CIM, -1, cim_intr_handler }, { 0 } }; - struct intr_info pl_intr_info = { + static const struct intr_info t7_pl_int_cause = { .name = "PL_INT_CAUSE", .cause_reg = A_PL_INT_CAUSE, .enable_reg = A_PL_INT_ENABLE, .fatal = 0, - .flags = 0, - .details = NULL, + .flags = IHF_IGNORE_IF_DISABLED, + .details = t7_pl_int_cause_fields, + .actions = t7_pl_int_cause_actions, + }; + static const struct intr_details t7_pl_int_cause2_fields[] = { + { F_CRYPTO_KEY, "CRYPTO KEY" }, + { F_CRYPTO1, "CRYPTO1" }, + { F_CRYPTO0, "CRYPTO0" }, + { F_GCACHE, "GCACHE" }, + { F_ARM, "ARM" }, + { 0 } + }; + static const struct intr_action t7_pl_int_cause2_actions[] = { + { F_CRYPTO_KEY, -1, cryptokey_intr_handler }, + { F_CRYPTO1, 1, tlstx_intr_handler }, + { F_CRYPTO0, 0, tlstx_intr_handler }, + { F_GCACHE, -1, gcache_intr_handler }, + { F_ARM, -1, arm_intr_handler }, + { 0 } + }; + static const struct intr_info t7_pl_int_cause2 = { + .name = "PL_INT_CAUSE2", + .cause_reg = A_PL_INT_CAUSE2, + .enable_reg = A_PL_INT_ENABLE2, + .fatal = 0, + .flags = IHF_IGNORE_IF_DISABLED, + .details = t7_pl_int_cause2_fields, + .actions = t7_pl_int_cause2_actions, + }; + static const struct intr_details t7_pl_perr_cause_fields[] = { + { F_T7_PL_PERR_CRYPTO_KEY, "CRYPTO KEY" }, + { F_T7_PL_PERR_CRYPTO1, "CRYPTO1" }, + { F_T7_PL_PERR_CRYPTO0, "CRYPTO0" }, + { F_T7_PL_PERR_GCACHE, "GCACHE" }, + { F_T7_PL_PERR_ARM, "ARM" }, + { F_T7_PL_PERR_ULP_TX, "ULP TX" }, + { F_T7_PL_PERR_SGE, "SGE" }, + { F_T7_PL_PERR_HMA, "HMA" }, + { F_T7_PL_PERR_CPL_SWITCH, "CPL Switch" }, + { F_T7_PL_PERR_ULP_RX, "ULP RX" }, + { F_T7_PL_PERR_PM_RX, "PM RX" }, + { F_T7_PL_PERR_PM_TX, "PM TX" }, + { F_T7_PL_PERR_MA, "MA" }, + { F_T7_PL_PERR_TP, "TP" }, + { F_T7_PL_PERR_LE, "LE" }, + { F_T7_PL_PERR_EDC1, "EDC1" }, + { F_T7_PL_PERR_EDC0, "EDC0" }, + { F_T7_PL_PERR_MC1, "MC1" }, + { F_T7_PL_PERR_MC0, "MC0" }, + { F_T7_PL_PERR_PCIE, "PCIE" }, + { F_T7_PL_PERR_UART, "UART" }, + { F_T7_PL_PERR_PMU, "PMU" }, + { F_T7_PL_PERR_MAC, "MAC" }, + { F_T7_PL_PERR_SMB, "SMB" }, + { F_T7_PL_PERR_SF, "SF" }, + { F_T7_PL_PERR_PL, "PL" }, + { F_T7_PL_PERR_NCSI, "NC-SI" }, + { F_T7_PL_PERR_MPS, "MPS" }, + { F_T7_PL_PERR_MI, "MI" }, + { F_T7_PL_PERR_DBG, "DBG" }, + { F_T7_PL_PERR_I2CM, "I2CM" }, + { F_T7_PL_PERR_CIM, "CIM" }, + { 0 } + }; + static const struct intr_info t7_pl_perr_cause = { + .name = "PL_PERR_CAUSE", + .cause_reg = A_PL_PERR_CAUSE, + .enable_reg = A_PL_PERR_ENABLE, + .fatal = 0xffffffff, + .flags = IHF_IGNORE_IF_DISABLED | IHF_FATAL_IFF_ENABLED, + .details = t7_pl_perr_cause_fields, .actions = NULL, }; - u32 perr; - - if (chip_id(adap) >= CHELSIO_T7) { - pl_perr_cause.details = t7_pl_intr_details; - pl_intr_info.details = t7_pl_intr_details; - pl_intr_info.actions = t7_pl_intr_action; + bool fatal = false; + uint32_t perr; + + if (chip_id(adap) < CHELSIO_T7) { + perr = get_perr_ucause(adap, &pl_perr_cause); + fatal |= t4_handle_intr(adap, &pl_perr_cause, 0, + flags & ~(IHF_CLR_ALL_SET | IHF_CLR_ALL_UNIGNORED)); + fatal |= t4_handle_intr(adap, &pl_int_cause, + t4_perr_to_ic(adap, perr), flags); + t4_write_reg(adap, pl_perr_cause.cause_reg, perr); + (void)t4_read_reg(adap, pl_perr_cause.cause_reg); } else { - pl_perr_cause.details = pl_intr_details; - pl_intr_info.details = pl_intr_details; - pl_intr_info.actions = pl_intr_action; + perr = get_perr_ucause(adap, &t7_pl_perr_cause); + fatal |= t4_handle_intr(adap, &t7_pl_perr_cause, 0, + flags & ~(IHF_CLR_ALL_SET | IHF_CLR_ALL_UNIGNORED)); + fatal |= t4_handle_intr(adap, &t7_pl_int_cause, + t7_perr_to_ic1(perr), flags); + fatal |= t4_handle_intr(adap, &t7_pl_int_cause2, + t7_perr_to_ic2(perr), flags); + t4_write_reg(adap, t7_pl_perr_cause.cause_reg, perr); + (void)t4_read_reg(adap, t7_pl_perr_cause.cause_reg); } - - perr = t4_read_reg(adap, pl_perr_cause.cause_reg); - if (verbose || perr != 0) { - t4_show_intr_info(adap, &pl_perr_cause, perr); - if (perr != 0) - t4_write_reg(adap, pl_perr_cause.cause_reg, perr); - if (verbose) - perr |= t4_read_reg(adap, pl_intr_info.enable_reg); - } - - return (t4_handle_intr(adap, &pl_intr_info, perr, verbose)); + return (fatal); } -#define PF_INTR_MASK (F_PFSW | F_PFCIM) +void t4_intr_clear(struct adapter *adap) +{ +#if 1 + if (chip_id(adap) >= CHELSIO_T7) + t4_write_reg(adap, A_SGE_INT_CAUSE8, 0xffffffff); +#endif + (void)t4_slow_intr_handler(adap, + IHF_NO_SHOW | IHF_RUN_ALL_ACTIONS | IHF_CLR_ALL_SET); +} /** * t4_intr_enable - enable interrupts @@ -6229,6 +7163,8 @@ void t4_intr_enable(struct adapter *adap) { u32 mask, val; + if (adap->intr_flags & IHF_INTR_CLEAR_ON_INIT) + t4_intr_clear(adap); if (chip_id(adap) <= CHELSIO_T5) val = F_ERR_DROPPED_DB | F_ERR_EGR_CTXT_PRIO | F_DBFIFO_HP_INT | F_DBFIFO_LP_INT; @@ -6241,8 +7177,14 @@ void t4_intr_enable(struct adapter *adap) F_ERR_BAD_DB_PIDX0 | F_ERR_ING_CTXT_PRIO | F_EGRESS_SIZE_ERR; mask = val; t4_set_reg_field(adap, A_SGE_INT_ENABLE3, mask, val); - t4_write_reg(adap, MYPF_REG(A_PL_PF_INT_ENABLE), PF_INTR_MASK); + if (chip_id(adap) >= CHELSIO_T7) + t4_write_reg(adap, A_SGE_INT_ENABLE4, 0xffffffff); + t4_write_reg(adap, MYPF_REG(A_PL_PF_INT_ENABLE), F_PFSW | F_PFCIM); t4_set_reg_field(adap, A_PL_INT_ENABLE, F_SF | F_I2CM, 0); +#if 1 + if (chip_id(adap) >= CHELSIO_T7) + t4_set_reg_field(adap, A_PL_INT_ENABLE, F_MAC0 | F_MAC1 | F_MAC2 | F_MAC3, 0); +#endif t4_set_reg_field(adap, A_PL_INT_MAP0, 0, 1 << adap->pf); } @@ -6439,9 +7381,15 @@ int t4_config_vi_rss(struct adapter *adapter, int mbox, unsigned int viid, /* Read an RSS table row */ static int rd_rss_row(struct adapter *adap, int row, u32 *val) { - t4_write_reg(adap, A_TP_RSS_LKP_TABLE, 0xfff00000 | row); - return t4_wait_op_done_val(adap, A_TP_RSS_LKP_TABLE, F_LKPTBLROWVLD, 1, - 5, 0, val); + if (chip_id(adap) < CHELSIO_T7) { + t4_write_reg(adap, A_TP_RSS_LKP_TABLE, 0xfff00000 | row); + return t4_wait_op_done_val(adap, A_TP_RSS_LKP_TABLE, + F_LKPTBLROWVLD, 1, 5, 0, val); + } else { + t4_write_reg(adap, A_TP_RSS_CONFIG_SRAM, 0xB0000 | row); + return t7_wait_sram_done(adap, A_TP_RSS_CONFIG_SRAM, + A_TP_RSS_LKP_TABLE, 5, 0, val); + } } /** @@ -10178,7 +11126,7 @@ const struct chip_params *t4_get_chip_params(int chipid) .vfcount = 256, .sge_fl_db = 0, .sge_ctxt_size = SGE_CTXT_SIZE_T7, - .mps_tcam_size = NUM_MPS_T5_CLS_SRAM_L_INSTANCES, + .mps_tcam_size = NUM_MPS_T5_CLS_SRAM_L_INSTANCES * 3, .rss_nentries = T7_RSS_NENTRIES, .cim_la_size = CIMLA_SIZE_T6, }, diff --git a/sys/dev/cxgbe/common/t4_msg.h b/sys/dev/cxgbe/common/t4_msg.h index 0d12ccf2e910..214080964fbb 100644 --- a/sys/dev/cxgbe/common/t4_msg.h +++ b/sys/dev/cxgbe/common/t4_msg.h @@ -30,6 +30,7 @@ #define T4_MSG_H enum cpl_opcodes { + CPL_TLS_TX_SCMD_FMT = 0x0, CPL_PASS_OPEN_REQ = 0x1, CPL_PASS_ACCEPT_RPL = 0x2, CPL_ACT_OPEN_REQ = 0x3, @@ -48,6 +49,8 @@ enum cpl_opcodes { CPL_RTE_READ_REQ = 0x11, CPL_L2T_WRITE_REQ = 0x12, CPL_L2T_READ_REQ = 0x13, + CPL_GRE_TABLE_REQ = 0x1b, + CPL_GRE_TABLE_RPL = 0xbb, CPL_SMT_WRITE_REQ = 0x14, CPL_SMT_READ_REQ = 0x15, CPL_TAG_WRITE_REQ = 0x16, @@ -130,6 +133,7 @@ enum cpl_opcodes { CPL_TX_TLS_SFO = 0x89, CPL_TX_SEC_PDU = 0x8A, CPL_TX_TLS_ACK = 0x8B, + CPL_TX_QUIC_ENC = 0x8d, CPL_RCB_UPD = 0x8C, CPL_SGE_FLR_FLUSH = 0xA0, @@ -258,6 +262,7 @@ enum { ULP_MODE_TCPDDP = 5, ULP_MODE_FCOE = 6, ULP_MODE_TLS = 8, + ULP_MODE_DTLS = 9, ULP_MODE_RDMA_V2 = 10, ULP_MODE_NVMET = 11, }; @@ -1149,23 +1154,36 @@ struct cpl_get_tcb { #define V_QUEUENO(x) ((x) << S_QUEUENO) #define G_QUEUENO(x) (((x) >> S_QUEUENO) & M_QUEUENO) -#define S_T7_QUEUENO 0 -#define M_T7_QUEUENO 0xFFF -#define V_T7_QUEUENO(x) ((x) << S_T7_QUEUENO) -#define G_T7_QUEUENO(x) (((x) >> S_T7_QUEUENO) & M_T7_QUEUENO) - #define S_REPLY_CHAN 14 #define V_REPLY_CHAN(x) ((x) << S_REPLY_CHAN) #define F_REPLY_CHAN V_REPLY_CHAN(1U) +#define S_NO_REPLY 15 +#define V_NO_REPLY(x) ((x) << S_NO_REPLY) +#define F_NO_REPLY V_NO_REPLY(1U) + +struct cpl_t7_get_tcb { + WR_HDR; + union opcode_tid ot; + __be16 rxchan_queue; + __be16 cookie_pkd; +}; + #define S_T7_REPLY_CHAN 12 #define M_T7_REPLY_CHAN 0x7 #define V_T7_REPLY_CHAN(x) ((x) << S_T7_REPLY_CHAN) #define G_T7_REPLY_CHAN(x) (((x) >> S_T7_REPLY_CHAN) & M_T7_REPLY_CHAN) -#define S_NO_REPLY 15 -#define V_NO_REPLY(x) ((x) << S_NO_REPLY) -#define F_NO_REPLY V_NO_REPLY(1U) +#define S_T7_QUEUENO 0 +#define M_T7_QUEUENO 0xFFF +#define V_T7_QUEUENO(x) ((x) << S_T7_QUEUENO) +#define G_T7_QUEUENO(x) (((x) >> S_T7_QUEUENO) & M_T7_QUEUENO) + +#define S_CPL_GET_TCB_COOKIE 0 +#define M_CPL_GET_TCB_COOKIE 0xff +#define V_CPL_GET_TCB_COOKIE(x) ((x) << S_CPL_GET_TCB_COOKIE) +#define G_CPL_GET_TCB_COOKIE(x) \ + (((x) >> S_CPL_GET_TCB_COOKIE) & M_CPL_GET_TCB_COOKIE) struct cpl_get_tcb_rpl { RSS_HDR @@ -1234,6 +1252,16 @@ struct cpl_close_con_rpl { __be32 rcv_nxt; }; +struct cpl_t7_close_con_rpl { + RSS_HDR + union opcode_tid ot; + __be16 rto; + __u8 rsvd; + __u8 status; + __be32 snd_nxt; + __be32 rcv_nxt; +}; + struct cpl_close_listsvr_req { WR_HDR; union opcode_tid ot; @@ -1340,6 +1368,24 @@ struct cpl_abort_rpl_rss { __u8 status; }; +struct cpl_t7_abort_rpl_rss { + RSS_HDR + union opcode_tid ot; + __be32 idx_status; +}; + +#define S_CPL_ABORT_RPL_RSS_IDX 8 +#define M_CPL_ABORT_RPL_RSS_IDX 0xffffff +#define V_CPL_ABORT_RPL_RSS_IDX(x) ((x) << S_CPL_ABORT_RPL_RSS_IDX) +#define G_CPL_ABORT_RPL_RSS_IDX(x) \ + (((x) >> S_CPL_ABORT_RPL_RSS_IDX) & M_CPL_ABORT_RPL_RSS_IDX) + +#define S_CPL_ABORT_RPL_RSS_STATUS 0 +#define M_CPL_ABORT_RPL_RSS_STATUS 0xff +#define V_CPL_ABORT_RPL_RSS_STATUS(x) ((x) << S_CPL_ABORT_RPL_RSS_STATUS) +#define G_CPL_ABORT_RPL_RSS_STATUS(x) \ + (((x) >> S_CPL_ABORT_RPL_RSS_STATUS) & M_CPL_ABORT_RPL_RSS_STATUS) + struct cpl_abort_rpl_rss6 { RSS_HDR union opcode_tid ot; @@ -1444,6 +1490,11 @@ struct cpl_tx_data { #define V_TX_ULP_MODE(x) ((x) << S_TX_ULP_MODE) #define G_TX_ULP_MODE(x) (((x) >> S_TX_ULP_MODE) & M_TX_ULP_MODE) +#define S_T7_TX_ULP_MODE 10 +#define M_T7_TX_ULP_MODE 0xf +#define V_T7_TX_ULP_MODE(x) ((x) << S_T7_TX_ULP_MODE) +#define G_T7_TX_ULP_MODE(x) (((x) >> S_T7_TX_ULP_MODE) & M_T7_TX_ULP_MODE) + #define S_TX_FORCE 13 #define V_TX_FORCE(x) ((x) << S_TX_FORCE) #define F_TX_FORCE V_TX_FORCE(1U) @@ -1881,14 +1932,6 @@ struct cpl_tx_pkt_xt { (((x) >> S_CPL_TX_PKT_XT_ROCEIPHDRLEN_HI) & \ M_CPL_TX_PKT_XT_ROCEIPHDRLEN_HI) -#define S_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO 30 -#define M_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO 0x3 -#define V_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO(x) \ - ((x) << S_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO) -#define G_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO(x) \ - (((x) >> S_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO) & \ - M_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO) - /* cpl_tx_pkt_xt.core.ctrl2 fields */ #define S_CPL_TX_PKT_XT_CHKINSRTOFFSET_LO 30 #define M_CPL_TX_PKT_XT_CHKINSRTOFFSET_LO 0x3 @@ -1898,6 +1941,14 @@ struct cpl_tx_pkt_xt { (((x) >> S_CPL_TX_PKT_XT_CHKINSRTOFFSET_LO) & \ M_CPL_TX_PKT_XT_CHKINSRTOFFSET_LO) +#define S_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO 30 +#define M_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO 0x3 +#define V_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO(x) \ + ((x) << S_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO) +#define G_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO(x) \ + (((x) >> S_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO) & \ + M_CPL_TX_PKT_XT_ROCEIPHDRLEN_LO) + #define S_CPL_TX_PKT_XT_CHKSTARTOFFSET 20 #define M_CPL_TX_PKT_XT_CHKSTARTOFFSET 0x3ff #define V_CPL_TX_PKT_XT_CHKSTARTOFFSET(x) \ @@ -2190,7 +2241,8 @@ struct cpl_t7_tx_data_iso { __be32 num_pi_bytes_seglen_offset; __be32 datasn_offset; __be32 buffer_offset; - __be32 reserved3; + __be32 pdo_pkd; + /* encapsulated CPL_TX_DATA follows here */ }; #define S_CPL_T7_TX_DATA_ISO_OPCODE 24 @@ -2274,6 +2326,12 @@ struct cpl_t7_tx_data_iso { (((x) >> S_CPL_T7_TX_DATA_ISO_DATASEGLENOFFSET) & \ M_CPL_T7_TX_DATA_ISO_DATASEGLENOFFSET) +#define S_CPL_TX_DATA_ISO_PDO 0 +#define M_CPL_TX_DATA_ISO_PDO 0xff +#define V_CPL_TX_DATA_ISO_PDO(x) ((x) << S_CPL_TX_DATA_ISO_PDO) +#define G_CPL_TX_DATA_ISO_PDO(x) \ + (((x) >> S_CPL_TX_DATA_ISO_PDO) & M_CPL_TX_DATA_ISO_PDO) + struct cpl_iscsi_hdr { RSS_HDR union opcode_tid ot; @@ -2419,6 +2477,74 @@ struct cpl_rx_data_ack_core { #define V_RX_DACK_CHANGE(x) ((x) << S_RX_DACK_CHANGE) #define F_RX_DACK_CHANGE V_RX_DACK_CHANGE(1U) +struct cpl_rx_phys_addr { + __be32 RSS[2]; + __be32 op_to_tid; + __be32 pci_rlx_order_to_len; + __be64 phys_addr; +}; + +#define S_CPL_RX_PHYS_ADDR_OPCODE 24 +#define M_CPL_RX_PHYS_ADDR_OPCODE 0xff +#define V_CPL_RX_PHYS_ADDR_OPCODE(x) ((x) << S_CPL_RX_PHYS_ADDR_OPCODE) +#define G_CPL_RX_PHYS_ADDR_OPCODE(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_OPCODE) & M_CPL_RX_PHYS_ADDR_OPCODE) + +#define S_CPL_RX_PHYS_ADDR_ISRDMA 23 +#define M_CPL_RX_PHYS_ADDR_ISRDMA 0x1 +#define V_CPL_RX_PHYS_ADDR_ISRDMA(x) ((x) << S_CPL_RX_PHYS_ADDR_ISRDMA) +#define G_CPL_RX_PHYS_ADDR_ISRDMA(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_ISRDMA) & M_CPL_RX_PHYS_ADDR_ISRDMA) +#define F_CPL_RX_PHYS_ADDR_ISRDMA V_CPL_RX_PHYS_ADDR_ISRDMA(1U) + +#define S_CPL_RX_PHYS_ADDR_TID 0 +#define M_CPL_RX_PHYS_ADDR_TID 0xfffff +#define V_CPL_RX_PHYS_ADDR_TID(x) ((x) << S_CPL_RX_PHYS_ADDR_TID) +#define G_CPL_RX_PHYS_ADDR_TID(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_TID) & M_CPL_RX_PHYS_ADDR_TID) + +#define S_CPL_RX_PHYS_ADDR_PCIRLXORDER 31 +#define M_CPL_RX_PHYS_ADDR_PCIRLXORDER 0x1 +#define V_CPL_RX_PHYS_ADDR_PCIRLXORDER(x) \ + ((x) << S_CPL_RX_PHYS_ADDR_PCIRLXORDER) +#define G_CPL_RX_PHYS_ADDR_PCIRLXORDER(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_PCIRLXORDER) & M_CPL_RX_PHYS_ADDR_PCIRLXORDER) +#define F_CPL_RX_PHYS_ADDR_PCIRLXORDER V_CPL_RX_PHYS_ADDR_PCIRLXORDER(1U) + +#define S_CPL_RX_PHYS_ADDR_PCINOSNOOP 30 +#define M_CPL_RX_PHYS_ADDR_PCINOSNOOP 0x1 +#define V_CPL_RX_PHYS_ADDR_PCINOSNOOP(x) \ + ((x) << S_CPL_RX_PHYS_ADDR_PCINOSNOOP) +#define G_CPL_RX_PHYS_ADDR_PCINOSNOOP(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_PCINOSNOOP) & M_CPL_RX_PHYS_ADDR_PCINOSNOOP) +#define F_CPL_RX_PHYS_ADDR_PCINOSNOOP V_CPL_RX_PHYS_ADDR_PCINOSNOOP(1U) + +#define S_CPL_RX_PHYS_ADDR_PCITPHINTEN 29 +#define M_CPL_RX_PHYS_ADDR_PCITPHINTEN 0x1 +#define V_CPL_RX_PHYS_ADDR_PCITPHINTEN(x) \ + ((x) << S_CPL_RX_PHYS_ADDR_PCITPHINTEN) +#define G_CPL_RX_PHYS_ADDR_PCITPHINTEN(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_PCITPHINTEN) & M_CPL_RX_PHYS_ADDR_PCITPHINTEN) +#define F_CPL_RX_PHYS_ADDR_PCITPHINTEN V_CPL_RX_PHYS_ADDR_PCITPHINTEN(1U) + +#define S_CPL_RX_PHYS_ADDR_PCITPHINT 27 +#define M_CPL_RX_PHYS_ADDR_PCITPHINT 0x3 +#define V_CPL_RX_PHYS_ADDR_PCITPHINT(x) ((x) << S_CPL_RX_PHYS_ADDR_PCITPHINT) +#define G_CPL_RX_PHYS_ADDR_PCITPHINT(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_PCITPHINT) & M_CPL_RX_PHYS_ADDR_PCITPHINT) + +#define S_CPL_RX_PHYS_ADDR_DCAID 16 +#define M_CPL_RX_PHYS_ADDR_DCAID 0x7ff +#define V_CPL_RX_PHYS_ADDR_DCAID(x) ((x) << S_CPL_RX_PHYS_ADDR_DCAID) +#define G_CPL_RX_PHYS_ADDR_DCAID(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_DCAID) & M_CPL_RX_PHYS_ADDR_DCAID) + +#define S_CPL_RX_PHYS_ADDR_LEN 0 +#define M_CPL_RX_PHYS_ADDR_LEN 0xffff +#define V_CPL_RX_PHYS_ADDR_LEN(x) ((x) << S_CPL_RX_PHYS_ADDR_LEN) +#define G_CPL_RX_PHYS_ADDR_LEN(x) \ + (((x) >> S_CPL_RX_PHYS_ADDR_LEN) & M_CPL_RX_PHYS_ADDR_LEN) + struct cpl_rx_ddp_complete { RSS_HDR union opcode_tid ot; @@ -4059,13 +4185,6 @@ struct cpl_rdma_cqe_ext { #define G_CPL_RDMA_CQE_EXT_QPID(x) \ (((x) >> S_CPL_RDMA_CQE_EXT_QPID) & M_CPL_RDMA_CQE_EXT_QPID) -#define S_CPL_RDMA_CQE_EXT_EXTMODE 11 -#define M_CPL_RDMA_CQE_EXT_EXTMODE 0x1 -#define V_CPL_RDMA_CQE_EXT_EXTMODE(x) ((x) << S_CPL_RDMA_CQE_EXT_EXTMODE) -#define G_CPL_RDMA_CQE_EXT_EXTMODE(x) \ - (((x) >> S_CPL_RDMA_CQE_EXT_EXTMODE) & M_CPL_RDMA_CQE_EXT_EXTMODE) -#define F_CPL_RDMA_CQE_EXT_EXTMODE V_CPL_RDMA_CQE_EXT_EXTMODE(1U) - #define S_CPL_RDMA_CQE_EXT_GENERATION_BIT 10 #define M_CPL_RDMA_CQE_EXT_GENERATION_BIT 0x1 #define V_CPL_RDMA_CQE_EXT_GENERATION_BIT(x) \ @@ -4109,6 +4228,13 @@ struct cpl_rdma_cqe_ext { #define G_CPL_RDMA_CQE_EXT_WR_TYPE_EXT(x) \ (((x) >> S_CPL_RDMA_CQE_EXT_WR_TYPE_EXT) & M_CPL_RDMA_CQE_EXT_WR_TYPE_EXT) +#define S_CPL_RDMA_CQE_EXT_EXTMODE 23 +#define M_CPL_RDMA_CQE_EXT_EXTMODE 0x1 +#define V_CPL_RDMA_CQE_EXT_EXTMODE(x) ((x) << S_CPL_RDMA_CQE_EXT_EXTMODE) +#define G_CPL_RDMA_CQE_EXT_EXTMODE(x) \ + (((x) >> S_CPL_RDMA_CQE_EXT_EXTMODE) & M_CPL_RDMA_CQE_EXT_EXTMODE) +#define F_CPL_RDMA_CQE_EXT_EXTMODE V_CPL_RDMA_CQE_EXT_EXTMODE(1U) + #define S_CPL_RDMA_CQE_EXT_SRQ 0 #define M_CPL_RDMA_CQE_EXT_SRQ 0xfff #define V_CPL_RDMA_CQE_EXT_SRQ(x) ((x) << S_CPL_RDMA_CQE_EXT_SRQ) @@ -4161,14 +4287,6 @@ struct cpl_rdma_cqe_fw_ext { #define G_CPL_RDMA_CQE_FW_EXT_QPID(x) \ (((x) >> S_CPL_RDMA_CQE_FW_EXT_QPID) & M_CPL_RDMA_CQE_FW_EXT_QPID) -#define S_CPL_RDMA_CQE_FW_EXT_EXTMODE 11 -#define M_CPL_RDMA_CQE_FW_EXT_EXTMODE 0x1 -#define V_CPL_RDMA_CQE_FW_EXT_EXTMODE(x) \ - ((x) << S_CPL_RDMA_CQE_FW_EXT_EXTMODE) -#define G_CPL_RDMA_CQE_FW_EXT_EXTMODE(x) \ - (((x) >> S_CPL_RDMA_CQE_FW_EXT_EXTMODE) & M_CPL_RDMA_CQE_FW_EXT_EXTMODE) -#define F_CPL_RDMA_CQE_FW_EXT_EXTMODE V_CPL_RDMA_CQE_FW_EXT_EXTMODE(1U) - #define S_CPL_RDMA_CQE_FW_EXT_GENERATION_BIT 10 #define M_CPL_RDMA_CQE_FW_EXT_GENERATION_BIT 0x1 #define V_CPL_RDMA_CQE_FW_EXT_GENERATION_BIT(x) \ @@ -4215,6 +4333,14 @@ struct cpl_rdma_cqe_fw_ext { (((x) >> S_CPL_RDMA_CQE_FW_EXT_WR_TYPE_EXT) & \ M_CPL_RDMA_CQE_FW_EXT_WR_TYPE_EXT) +#define S_CPL_RDMA_CQE_FW_EXT_EXTMODE 23 +#define M_CPL_RDMA_CQE_FW_EXT_EXTMODE 0x1 +#define V_CPL_RDMA_CQE_FW_EXT_EXTMODE(x) \ + ((x) << S_CPL_RDMA_CQE_FW_EXT_EXTMODE) +#define G_CPL_RDMA_CQE_FW_EXT_EXTMODE(x) \ + (((x) >> S_CPL_RDMA_CQE_FW_EXT_EXTMODE) & M_CPL_RDMA_CQE_FW_EXT_EXTMODE) +#define F_CPL_RDMA_CQE_FW_EXT_EXTMODE V_CPL_RDMA_CQE_FW_EXT_EXTMODE(1U) + #define S_CPL_RDMA_CQE_FW_EXT_SRQ 0 #define M_CPL_RDMA_CQE_FW_EXT_SRQ 0xfff #define V_CPL_RDMA_CQE_FW_EXT_SRQ(x) ((x) << S_CPL_RDMA_CQE_FW_EXT_SRQ) @@ -4267,14 +4393,6 @@ struct cpl_rdma_cqe_err_ext { #define G_CPL_RDMA_CQE_ERR_EXT_QPID(x) \ (((x) >> S_CPL_RDMA_CQE_ERR_EXT_QPID) & M_CPL_RDMA_CQE_ERR_EXT_QPID) -#define S_CPL_RDMA_CQE_ERR_EXT_EXTMODE 11 -#define M_CPL_RDMA_CQE_ERR_EXT_EXTMODE 0x1 -#define V_CPL_RDMA_CQE_ERR_EXT_EXTMODE(x) \ - ((x) << S_CPL_RDMA_CQE_ERR_EXT_EXTMODE) -#define G_CPL_RDMA_CQE_ERR_EXT_EXTMODE(x) \ - (((x) >> S_CPL_RDMA_CQE_ERR_EXT_EXTMODE) & M_CPL_RDMA_CQE_ERR_EXT_EXTMODE) -#define F_CPL_RDMA_CQE_ERR_EXT_EXTMODE V_CPL_RDMA_CQE_ERR_EXT_EXTMODE(1U) - #define S_CPL_RDMA_CQE_ERR_EXT_GENERATION_BIT 10 #define M_CPL_RDMA_CQE_ERR_EXT_GENERATION_BIT 0x1 #define V_CPL_RDMA_CQE_ERR_EXT_GENERATION_BIT(x) \ @@ -4323,6 +4441,14 @@ struct cpl_rdma_cqe_err_ext { (((x) >> S_CPL_RDMA_CQE_ERR_EXT_WR_TYPE_EXT) & \ M_CPL_RDMA_CQE_ERR_EXT_WR_TYPE_EXT) +#define S_CPL_RDMA_CQE_ERR_EXT_EXTMODE 23 +#define M_CPL_RDMA_CQE_ERR_EXT_EXTMODE 0x1 +#define V_CPL_RDMA_CQE_ERR_EXT_EXTMODE(x) \ + ((x) << S_CPL_RDMA_CQE_ERR_EXT_EXTMODE) +#define G_CPL_RDMA_CQE_ERR_EXT_EXTMODE(x) \ + (((x) >> S_CPL_RDMA_CQE_ERR_EXT_EXTMODE) & M_CPL_RDMA_CQE_ERR_EXT_EXTMODE) +#define F_CPL_RDMA_CQE_ERR_EXT_EXTMODE V_CPL_RDMA_CQE_ERR_EXT_EXTMODE(1U) + #define S_CPL_RDMA_CQE_ERR_EXT_SRQ 0 #define M_CPL_RDMA_CQE_ERR_EXT_SRQ 0xfff #define V_CPL_RDMA_CQE_ERR_EXT_SRQ(x) ((x) << S_CPL_RDMA_CQE_ERR_EXT_SRQ) @@ -5040,6 +5166,58 @@ struct cpl_tx_tnl_lso { #define G_CPL_TX_TNL_LSO_SIZE(x) \ (((x) >> S_CPL_TX_TNL_LSO_SIZE) & M_CPL_TX_TNL_LSO_SIZE) +#define S_CPL_TX_TNL_LSO_BTH_OPCODE 24 +#define M_CPL_TX_TNL_LSO_BTH_OPCODE 0xff +#define V_CPL_TX_TNL_LSO_BTH_OPCODE(x) ((x) << S_CPL_TX_TNL_LSO_BTH_OPCODE) +#define G_CPL_TX_TNL_LSO_BTH_OPCODE(x) \ + (((x) >> S_CPL_TX_TNL_LSO_BTH_OPCODE) & \ + M_CPL_TX_TNL_LSO_BTH_OPCODE) + +#define S_CPL_TX_TNL_LSO_TCPSEQOFFSET_PSN 0 +#define M_CPL_TX_TNL_LSO_TCPSEQOFFSET_PSN 0xffffff +#define V_CPL_TX_TNL_LSO_TCPSEQOFFSET_PSN(x) \ + ((x) << S_CPL_TX_TNL_LSO_TCPSEQOFFSET_PSN) +#define G_CPL_TX_TNL_LSO_TCPSEQOFFSET_PSN(x) \ + (((x) >> S_CPL_TX_TNL_LSO_TCPSEQOFFSET_PSN) & \ + M_CPL_TX_TNL_LSO_TCPSEQOFFSET_PSN) + +#define S_CPL_TX_TNL_LSO_MSS_TVER 8 +#define M_CPL_TX_TNL_LSO_MSS_TVER 0xf +#define V_CPL_TX_TNL_LSO_MSS_TVER(x) ((x) << S_CPL_TX_TNL_LSO_MSS_TVER) +#define G_CPL_TX_TNL_LSO_MSS_TVER(x) \ + (((x) >> S_CPL_TX_TNL_LSO_MSS_TVER) & M_CPL_TX_TNL_LSO_MSS_TVER) + +#define S_CPL_TX_TNL_LSO_MSS_M 7 +#define M_CPL_TX_TNL_LSO_MSS_M 0x1 +#define V_CPL_TX_TNL_LSO_MSS_M(x) ((x) << S_CPL_TX_TNL_LSO_MSS_M) +#define G_CPL_TX_TNL_LSO_MSS_M(x) \ + (((x) >> S_CPL_TX_TNL_LSO_MSS_M) & M_CPL_TX_TNL_LSO_MSS_M) + +#define S_CPL_TX_TNL_LSO_MSS_PMTU 4 +#define M_CPL_TX_TNL_LSO_MSS_PMTU 0x7 +#define V_CPL_TX_TNL_LSO_MSS_PMTU(x) ((x) << S_CPL_TX_TNL_LSO_MSS_PMTU) +#define G_CPL_TX_TNL_LSO_MSS_PMTU(x) \ + (((x) >> S_CPL_TX_TNL_LSO_MSS_PMTU) & M_CPL_TX_TNL_LSO_MSS_PMTU) + +#define S_CPL_TX_TNL_LSO_MSS_RR_MSN_INCR 3 +#define M_CPL_TX_TNL_LSO_MSS_RR_MSN_INCR 0x1 +#define V_CPL_TX_TNL_LSO_MSS_RR_MSN_INCR(x) \ + ((x) << S_CPL_TX_TNL_LSO_MSS_RR_MSN_INCR) +#define G_CPL_TX_TNL_LSO_MSS_RR_MSN_INCR(x) \ + (((x) >> S_CPL_TX_TNL_LSO_MSS_RR_MSN_INCR) & M_CPL_TX_TNL_LSO_MSS_RR_MSN_INCR) + +#define S_CPL_TX_TNL_LSO_MSS_ACKREQ 1 +#define M_CPL_TX_TNL_LSO_MSS_ACKREQ 0x3 +#define V_CPL_TX_TNL_LSO_MSS_ACKREQ(x) ((x) << S_CPL_TX_TNL_LSO_MSS_ACKREQ) +#define G_CPL_TX_TNL_LSO_MSS_ACKREQ(x) \ + (((x) >> S_CPL_TX_TNL_LSO_MSS_ACKREQ) & M_CPL_TX_TNL_LSO_MSS_ACKREQ) + +#define S_CPL_TX_TNL_LSO_MSS_SE 0 +#define M_CPL_TX_TNL_LSO_MSS_SE 0x1 +#define V_CPL_TX_TNL_LSO_MSS_SE(x) ((x) << S_CPL_TX_TNL_LSO_MSS_SE) +#define G_CPL_TX_TNL_LSO_MSS_SE(x) \ + (((x) >> S_CPL_TX_TNL_LSO_MSS_SE) & M_CPL_TX_TNL_LSO_MSS_SE) + struct cpl_rx_mps_pkt { __be32 op_to_r1_hi; __be32 r1_lo_length; @@ -5839,10 +6017,10 @@ struct cpl_tx_tls_ack { #define G_CPL_TX_TLS_ACK_OPCODE(x) \ (((x) >> S_CPL_TX_TLS_ACK_OPCODE) & M_CPL_TX_TLS_ACK_OPCODE) -#define S_T7_CPL_TX_TLS_ACK_RXCHID 22 -#define M_T7_CPL_TX_TLS_ACK_RXCHID 0x3 -#define V_T7_CPL_TX_TLS_ACK_RXCHID(x) ((x) << S_T7_CPL_TX_TLS_ACK_RXCHID) -#define G_T7_CPL_TX_TLS_ACK_RXCHID(x) \ +#define S_T7_CPL_TX_TLS_ACK_RXCHID 22 +#define M_T7_CPL_TX_TLS_ACK_RXCHID 0x3 +#define V_T7_CPL_TX_TLS_ACK_RXCHID(x) ((x) << S_T7_CPL_TX_TLS_ACK_RXCHID) +#define G_T7_CPL_TX_TLS_ACK_RXCHID(x) \ (((x) >> S_T7_CPL_TX_TLS_ACK_RXCHID) & M_T7_CPL_TX_TLS_ACK_RXCHID) #define S_CPL_TX_TLS_ACK_RXCHID 22 @@ -5905,11 +6083,245 @@ struct cpl_tx_tls_ack { #define G_CPL_TX_TLS_ACK_PLDLEN(x) \ (((x) >> S_CPL_TX_TLS_ACK_PLDLEN) & M_CPL_TX_TLS_ACK_PLDLEN) +struct cpl_tx_quic_enc { + __be32 op_to_hdrlen; + __be32 hdrlen_to_pktlen; + __be32 r4[2]; +}; + +#define S_CPL_TX_QUIC_ENC_OPCODE 24 +#define M_CPL_TX_QUIC_ENC_OPCODE 0xff +#define V_CPL_TX_QUIC_ENC_OPCODE(x) ((x) << S_CPL_TX_QUIC_ENC_OPCODE) +#define G_CPL_TX_QUIC_ENC_OPCODE(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_OPCODE) & M_CPL_TX_QUIC_ENC_OPCODE) + +#define S_CPL_TX_QUIC_ENC_KEYSIZE 22 +#define M_CPL_TX_QUIC_ENC_KEYSIZE 0x3 +#define V_CPL_TX_QUIC_ENC_KEYSIZE(x) ((x) << S_CPL_TX_QUIC_ENC_KEYSIZE) +#define G_CPL_TX_QUIC_ENC_KEYSIZE(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_KEYSIZE) & M_CPL_TX_QUIC_ENC_KEYSIZE) + +#define S_CPL_TX_QUIC_ENC_PKTNUMSIZE 20 +#define M_CPL_TX_QUIC_ENC_PKTNUMSIZE 0x3 +#define V_CPL_TX_QUIC_ENC_PKTNUMSIZE(x) ((x) << S_CPL_TX_QUIC_ENC_PKTNUMSIZE) +#define G_CPL_TX_QUIC_ENC_PKTNUMSIZE(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_PKTNUMSIZE) & M_CPL_TX_QUIC_ENC_PKTNUMSIZE) + +#define S_CPL_TX_QUIC_ENC_HDRTYPE 19 +#define M_CPL_TX_QUIC_ENC_HDRTYPE 0x1 +#define V_CPL_TX_QUIC_ENC_HDRTYPE(x) ((x) << S_CPL_TX_QUIC_ENC_HDRTYPE) +#define G_CPL_TX_QUIC_ENC_HDRTYPE(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_HDRTYPE) & M_CPL_TX_QUIC_ENC_HDRTYPE) +#define F_CPL_TX_QUIC_ENC_HDRTYPE V_CPL_TX_QUIC_ENC_HDRTYPE(1U) + +#define S_CPL_TX_QUIC_ENC_HDRSTARTOFFSET 4 +#define M_CPL_TX_QUIC_ENC_HDRSTARTOFFSET 0xfff +#define V_CPL_TX_QUIC_ENC_HDRSTARTOFFSET(x) \ + ((x) << S_CPL_TX_QUIC_ENC_HDRSTARTOFFSET) +#define G_CPL_TX_QUIC_ENC_HDRSTARTOFFSET(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_HDRSTARTOFFSET) & \ + M_CPL_TX_QUIC_ENC_HDRSTARTOFFSET) + +#define S_CPL_TX_QUIC_ENC_HDRLENGTH_HI 0 +#define M_CPL_TX_QUIC_ENC_HDRLENGTH_HI 0x3 +#define V_CPL_TX_QUIC_ENC_HDRLENGTH_HI(x) \ + ((x) << S_CPL_TX_QUIC_ENC_HDRLENGTH_HI) +#define G_CPL_TX_QUIC_ENC_HDRLENGTH_HI(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_HDRLENGTH_HI) & M_CPL_TX_QUIC_ENC_HDRLENGTH_HI) + +#define S_CPL_TX_QUIC_ENC_HDRLENGTH_LO 24 +#define M_CPL_TX_QUIC_ENC_HDRLENGTH_LO 0xff +#define V_CPL_TX_QUIC_ENC_HDRLENGTH_LO(x) \ + ((x) << S_CPL_TX_QUIC_ENC_HDRLENGTH_LO) +#define G_CPL_TX_QUIC_ENC_HDRLENGTH_LO(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_HDRLENGTH_LO) & M_CPL_TX_QUIC_ENC_HDRLENGTH_LO) + +#define S_CPL_TX_QUIC_ENC_NUMPKT 16 +#define M_CPL_TX_QUIC_ENC_NUMPKT 0xff +#define V_CPL_TX_QUIC_ENC_NUMPKT(x) ((x) << S_CPL_TX_QUIC_ENC_NUMPKT) +#define G_CPL_TX_QUIC_ENC_NUMPKT(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_NUMPKT) & M_CPL_TX_QUIC_ENC_NUMPKT) + +#define S_CPL_TX_QUIC_ENC_PKTLEN 0 +#define M_CPL_TX_QUIC_ENC_PKTLEN 0xffff +#define V_CPL_TX_QUIC_ENC_PKTLEN(x) ((x) << S_CPL_TX_QUIC_ENC_PKTLEN) +#define G_CPL_TX_QUIC_ENC_PKTLEN(x) \ + (((x) >> S_CPL_TX_QUIC_ENC_PKTLEN) & M_CPL_TX_QUIC_ENC_PKTLEN) + +struct cpl_tls_tx_scmd_fmt { + __be32 op_to_num_ivs; + __be32 enb_dbgId_to_hdrlen; + __be32 seq_num[2]; +}; + +#define S_CPL_TLS_TX_SCMD_FMT_OPCODE 31 +#define M_CPL_TLS_TX_SCMD_FMT_OPCODE 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_OPCODE(x) ((x) << S_CPL_TLS_TX_SCMD_FMT_OPCODE) +#define G_CPL_TLS_TX_SCMD_FMT_OPCODE(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_OPCODE) & M_CPL_TLS_TX_SCMD_FMT_OPCODE) +#define F_CPL_TLS_TX_SCMD_FMT_OPCODE V_CPL_TLS_TX_SCMD_FMT_OPCODE(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_SEQNUMBERCTRL 29 +#define M_CPL_TLS_TX_SCMD_FMT_SEQNUMBERCTRL 0x3 +#define V_CPL_TLS_TX_SCMD_FMT_SEQNUMBERCTRL(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_SEQNUMBERCTRL) +#define G_CPL_TLS_TX_SCMD_FMT_SEQNUMBERCTRL(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_SEQNUMBERCTRL) & \ + M_CPL_TLS_TX_SCMD_FMT_SEQNUMBERCTRL) + +#define S_CPL_TLS_TX_SCMD_FMT_PROTOVERSION 24 +#define M_CPL_TLS_TX_SCMD_FMT_PROTOVERSION 0xf +#define V_CPL_TLS_TX_SCMD_FMT_PROTOVERSION(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_PROTOVERSION) +#define G_CPL_TLS_TX_SCMD_FMT_PROTOVERSION(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_PROTOVERSION) & \ + M_CPL_TLS_TX_SCMD_FMT_PROTOVERSION) + +#define S_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL 23 +#define M_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL) +#define G_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL) & \ + M_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL) +#define F_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL V_CPL_TLS_TX_SCMD_FMT_ENCDECCTRL(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL 22 +#define M_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL) +#define G_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL) & \ + M_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL) +#define F_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL \ + V_CPL_TLS_TX_SCMD_FMT_CIPHAUTHSEQCTRL(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_CIPHMODE 18 +#define M_CPL_TLS_TX_SCMD_FMT_CIPHMODE 0xf +#define V_CPL_TLS_TX_SCMD_FMT_CIPHMODE(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_CIPHMODE) +#define G_CPL_TLS_TX_SCMD_FMT_CIPHMODE(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_CIPHMODE) & M_CPL_TLS_TX_SCMD_FMT_CIPHMODE) + +#define S_CPL_TLS_TX_SCMD_FMT_AUTHMODE 14 +#define M_CPL_TLS_TX_SCMD_FMT_AUTHMODE 0xf +#define V_CPL_TLS_TX_SCMD_FMT_AUTHMODE(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_AUTHMODE) +#define G_CPL_TLS_TX_SCMD_FMT_AUTHMODE(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_AUTHMODE) & M_CPL_TLS_TX_SCMD_FMT_AUTHMODE) + +#define S_CPL_TLS_TX_SCMD_FMT_HMACCTRL 11 +#define M_CPL_TLS_TX_SCMD_FMT_HMACCTRL 0x7 +#define V_CPL_TLS_TX_SCMD_FMT_HMACCTRL(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_HMACCTRL) +#define G_CPL_TLS_TX_SCMD_FMT_HMACCTRL(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_HMACCTRL) & M_CPL_TLS_TX_SCMD_FMT_HMACCTRL) + +#define S_CPL_TLS_TX_SCMD_FMT_IVSIZE 7 +#define M_CPL_TLS_TX_SCMD_FMT_IVSIZE 0xf +#define V_CPL_TLS_TX_SCMD_FMT_IVSIZE(x) ((x) << S_CPL_TLS_TX_SCMD_FMT_IVSIZE) +#define G_CPL_TLS_TX_SCMD_FMT_IVSIZE(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_IVSIZE) & M_CPL_TLS_TX_SCMD_FMT_IVSIZE) + +#define S_CPL_TLS_TX_SCMD_FMT_NUMIVS 0 +#define M_CPL_TLS_TX_SCMD_FMT_NUMIVS 0x7f +#define V_CPL_TLS_TX_SCMD_FMT_NUMIVS(x) ((x) << S_CPL_TLS_TX_SCMD_FMT_NUMIVS) +#define G_CPL_TLS_TX_SCMD_FMT_NUMIVS(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_NUMIVS) & M_CPL_TLS_TX_SCMD_FMT_NUMIVS) + +#define S_CPL_TLS_TX_SCMD_FMT_ENBDBGID 31 +#define M_CPL_TLS_TX_SCMD_FMT_ENBDBGID 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_ENBDBGID(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_ENBDBGID) +#define G_CPL_TLS_TX_SCMD_FMT_ENBDBGID(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_ENBDBGID) & M_CPL_TLS_TX_SCMD_FMT_ENBDBGID) +#define F_CPL_TLS_TX_SCMD_FMT_ENBDBGID V_CPL_TLS_TX_SCMD_FMT_ENBDBGID(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_IVGENCTRL 30 +#define M_CPL_TLS_TX_SCMD_FMT_IVGENCTRL 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_IVGENCTRL(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_IVGENCTRL) +#define G_CPL_TLS_TX_SCMD_FMT_IVGENCTRL(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_IVGENCTRL) & \ + M_CPL_TLS_TX_SCMD_FMT_IVGENCTRL) + +#define S_CPL_TLS_TX_SCMD_FMT_MOREFRAGS 20 +#define M_CPL_TLS_TX_SCMD_FMT_MOREFRAGS 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_MOREFRAGS(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_MOREFRAGS) +#define G_CPL_TLS_TX_SCMD_FMT_MOREFRAGS(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_MOREFRAGS) & \ + M_CPL_TLS_TX_SCMD_FMT_MOREFRAGS) +#define F_CPL_TLS_TX_SCMD_FMT_MOREFRAGS V_CPL_TLS_TX_SCMD_FMT_MOREFRAGS(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_LASTFRAGS 19 +#define M_CPL_TLS_TX_SCMD_FMT_LASTFRAGS 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_LASTFRAGS(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_LASTFRAGS) +#define G_CPL_TLS_TX_SCMD_FMT_LASTFRAGS(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_LASTFRAGS) & \ + M_CPL_TLS_TX_SCMD_FMT_LASTFRAGS) +#define F_CPL_TLS_TX_SCMD_FMT_LASTFRAGS V_CPL_TLS_TX_SCMD_FMT_LASTFRAGS(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU 18 +#define M_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU) +#define G_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU) & \ + M_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU) +#define F_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU V_CPL_TLS_TX_SCMD_FMT_TLSCOMPPDU(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY 17 +#define M_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY) +#define G_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY) & \ + M_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY) +#define F_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY \ + V_CPL_TLS_TX_SCMD_FMT_PAYLOADONLY(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE 16 +#define M_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE) +#define G_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE) & \ + M_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE) +#define F_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE \ + V_CPL_TLS_TX_SCMD_FMT_TLSFRAGENABLE(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_MACONLY 15 +#define M_CPL_TLS_TX_SCMD_FMT_MACONLY 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_MACONLY(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_MACONLY) +#define G_CPL_TLS_TX_SCMD_FMT_MACONLY(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_MACONLY) & M_CPL_TLS_TX_SCMD_FMT_MACONLY) +#define F_CPL_TLS_TX_SCMD_FMT_MACONLY V_CPL_TLS_TX_SCMD_FMT_MACONLY(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_AADIVDROP 14 +#define M_CPL_TLS_TX_SCMD_FMT_AADIVDROP 0x1 +#define V_CPL_TLS_TX_SCMD_FMT_AADIVDROP(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_AADIVDROP) +#define G_CPL_TLS_TX_SCMD_FMT_AADIVDROP(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_AADIVDROP) & \ + M_CPL_TLS_TX_SCMD_FMT_AADIVDROP) +#define F_CPL_TLS_TX_SCMD_FMT_AADIVDROP V_CPL_TLS_TX_SCMD_FMT_AADIVDROP(1U) + +#define S_CPL_TLS_TX_SCMD_FMT_HDRLENGTH 0 +#define M_CPL_TLS_TX_SCMD_FMT_HDRLENGTH 0x3fff +#define V_CPL_TLS_TX_SCMD_FMT_HDRLENGTH(x) \ + ((x) << S_CPL_TLS_TX_SCMD_FMT_HDRLENGTH) +#define G_CPL_TLS_TX_SCMD_FMT_HDRLENGTH(x) \ + (((x) >> S_CPL_TLS_TX_SCMD_FMT_HDRLENGTH) & \ + M_CPL_TLS_TX_SCMD_FMT_HDRLENGTH) + struct cpl_rcb_upd { __be32 op_to_tid; __be32 opcode_psn; __u8 nodata_to_cnprepclr; - __u8 r0; + __u8 rsp_nak_seqclr_pkd; __be16 wrptr; __be32 length; }; @@ -6202,13 +6614,6 @@ struct cpl_roce_cqe { #define G_CPL_ROCE_CQE_QPID(x) \ (((x) >> S_CPL_ROCE_CQE_QPID) & M_CPL_ROCE_CQE_QPID) -#define S_CPL_ROCE_CQE_EXTMODE 11 -#define M_CPL_ROCE_CQE_EXTMODE 0x1 -#define V_CPL_ROCE_CQE_EXTMODE(x) ((x) << S_CPL_ROCE_CQE_EXTMODE) -#define G_CPL_ROCE_CQE_EXTMODE(x) \ - (((x) >> S_CPL_ROCE_CQE_EXTMODE) & M_CPL_ROCE_CQE_EXTMODE) -#define F_CPL_ROCE_CQE_EXTMODE V_CPL_ROCE_CQE_EXTMODE(1U) - #define S_CPL_ROCE_CQE_GENERATION_BIT 10 #define M_CPL_ROCE_CQE_GENERATION_BIT 0x1 #define V_CPL_ROCE_CQE_GENERATION_BIT(x) \ @@ -6249,6 +6654,13 @@ struct cpl_roce_cqe { #define G_CPL_ROCE_CQE_WR_TYPE_EXT(x) \ (((x) >> S_CPL_ROCE_CQE_WR_TYPE_EXT) & M_CPL_ROCE_CQE_WR_TYPE_EXT) +#define S_CPL_ROCE_CQE_EXTMODE 23 +#define M_CPL_ROCE_CQE_EXTMODE 0x1 +#define V_CPL_ROCE_CQE_EXTMODE(x) ((x) << S_CPL_ROCE_CQE_EXTMODE) +#define G_CPL_ROCE_CQE_EXTMODE(x) \ + (((x) >> S_CPL_ROCE_CQE_EXTMODE) & M_CPL_ROCE_CQE_EXTMODE) +#define F_CPL_ROCE_CQE_EXTMODE V_CPL_ROCE_CQE_EXTMODE(1U) + #define S_CPL_ROCE_CQE_SRQ 0 #define M_CPL_ROCE_CQE_SRQ 0xfff #define V_CPL_ROCE_CQE_SRQ(x) ((x) << S_CPL_ROCE_CQE_SRQ) @@ -6304,13 +6716,6 @@ struct cpl_roce_cqe_fw { #define G_CPL_ROCE_CQE_FW_QPID(x) \ (((x) >> S_CPL_ROCE_CQE_FW_QPID) & M_CPL_ROCE_CQE_FW_QPID) -#define S_CPL_ROCE_CQE_FW_EXTMODE 11 -#define M_CPL_ROCE_CQE_FW_EXTMODE 0x1 -#define V_CPL_ROCE_CQE_FW_EXTMODE(x) ((x) << S_CPL_ROCE_CQE_FW_EXTMODE) -#define G_CPL_ROCE_CQE_FW_EXTMODE(x) \ - (((x) >> S_CPL_ROCE_CQE_FW_EXTMODE) & M_CPL_ROCE_CQE_FW_EXTMODE) -#define F_CPL_ROCE_CQE_FW_EXTMODE V_CPL_ROCE_CQE_FW_EXTMODE(1U) - #define S_CPL_ROCE_CQE_FW_GENERATION_BIT 10 #define M_CPL_ROCE_CQE_FW_GENERATION_BIT 0x1 #define V_CPL_ROCE_CQE_FW_GENERATION_BIT(x) \ @@ -6353,6 +6758,14 @@ struct cpl_roce_cqe_fw { #define G_CPL_ROCE_CQE_FW_WR_TYPE_EXT(x) \ (((x) >> S_CPL_ROCE_CQE_FW_WR_TYPE_EXT) & M_CPL_ROCE_CQE_FW_WR_TYPE_EXT) +#define S_CPL_ROCE_CQE_FW_EXTMODE 23 +#define M_CPL_ROCE_CQE_FW_EXTMODE 0x1 +#define V_CPL_ROCE_CQE_FW_EXTMODE(x) ((x) << S_CPL_ROCE_CQE_FW_EXTMODE) +#define G_CPL_ROCE_CQE_FW_EXTMODE(x) \ + (((x) >> S_CPL_ROCE_CQE_FW_EXTMODE) & M_CPL_ROCE_CQE_FW_EXTMODE) +#define F_CPL_ROCE_CQE_FW_EXTMODE V_CPL_ROCE_CQE_FW_EXTMODE(1U) + + #define S_CPL_ROCE_CQE_FW_SRQ 0 #define M_CPL_ROCE_CQE_FW_SRQ 0xfff #define V_CPL_ROCE_CQE_FW_SRQ(x) ((x) << S_CPL_ROCE_CQE_FW_SRQ) @@ -6360,16 +6773,16 @@ struct cpl_roce_cqe_fw { (((x) >> S_CPL_ROCE_CQE_FW_SRQ) & M_CPL_ROCE_CQE_FW_SRQ) struct cpl_roce_cqe_err { - __be32 op_to_CQID; - __be32 Tid_FlitCnt; - __be32 QPID_to_WR_type; - __be32 Length; - __be32 TAG; - __be32 MSN; - __be32 SE_to_SRQ; - __be32 RQE; - __be32 ExtInfoMS[2]; - __be32 ExtInfoLS[2]; + __be32 op_to_cqid; + __be32 tid_flitcnt; + __be32 qpid_to_wr_type; + __be32 length; + __be32 tag; + __be32 msn; + __be32 se_to_srq; + __be32 rqe; + __be32 extinfoms[2]; + __be32 extinfols[2]; }; #define S_CPL_ROCE_CQE_ERR_OPCODE 24 @@ -6408,13 +6821,6 @@ struct cpl_roce_cqe_err { #define G_CPL_ROCE_CQE_ERR_QPID(x) \ (((x) >> S_CPL_ROCE_CQE_ERR_QPID) & M_CPL_ROCE_CQE_ERR_QPID) -#define S_CPL_ROCE_CQE_ERR_EXTMODE 11 -#define M_CPL_ROCE_CQE_ERR_EXTMODE 0x1 -#define V_CPL_ROCE_CQE_ERR_EXTMODE(x) ((x) << S_CPL_ROCE_CQE_ERR_EXTMODE) -#define G_CPL_ROCE_CQE_ERR_EXTMODE(x) \ - (((x) >> S_CPL_ROCE_CQE_ERR_EXTMODE) & M_CPL_ROCE_CQE_ERR_EXTMODE) -#define F_CPL_ROCE_CQE_ERR_EXTMODE V_CPL_ROCE_CQE_ERR_EXTMODE(1U) - #define S_CPL_ROCE_CQE_ERR_GENERATION_BIT 10 #define M_CPL_ROCE_CQE_ERR_GENERATION_BIT 0x1 #define V_CPL_ROCE_CQE_ERR_GENERATION_BIT(x) \ @@ -6458,6 +6864,14 @@ struct cpl_roce_cqe_err { #define G_CPL_ROCE_CQE_ERR_WR_TYPE_EXT(x) \ (((x) >> S_CPL_ROCE_CQE_ERR_WR_TYPE_EXT) & M_CPL_ROCE_CQE_ERR_WR_TYPE_EXT) +#define S_CPL_ROCE_CQE_ERR_EXTMODE 23 +#define M_CPL_ROCE_CQE_ERR_EXTMODE 0x1 +#define V_CPL_ROCE_CQE_ERR_EXTMODE(x) ((x) << S_CPL_ROCE_CQE_ERR_EXTMODE) +#define G_CPL_ROCE_CQE_ERR_EXTMODE(x) \ + (((x) >> S_CPL_ROCE_CQE_ERR_EXTMODE) & M_CPL_ROCE_CQE_ERR_EXTMODE) +#define F_CPL_ROCE_CQE_ERR_EXTMODE V_CPL_ROCE_CQE_ERR_EXTMODE(1U) + + #define S_CPL_ROCE_CQE_ERR_SRQ 0 #define M_CPL_ROCE_CQE_ERR_SRQ 0xfff #define V_CPL_ROCE_CQE_ERR_SRQ(x) ((x) << S_CPL_ROCE_CQE_ERR_SRQ) diff --git a/sys/dev/cxgbe/common/t4_regs.h b/sys/dev/cxgbe/common/t4_regs.h index 8f500ec0fbdd..51f150443261 100644 --- a/sys/dev/cxgbe/common/t4_regs.h +++ b/sys/dev/cxgbe/common/t4_regs.h @@ -27,11 +27,11 @@ */ /* This file is automatically generated --- changes will be lost */ -/* Generation Date : Thu Sep 11 05:25:56 PM IST 2025 */ +/* Generation Date : Tue Oct 28 05:23:45 PM IST 2025 */ /* Directory name: t4_reg.txt, Date: Not specified */ /* Directory name: t5_reg.txt, Changeset: 6945:54ba4ba7ee8b */ /* Directory name: t6_reg.txt, Changeset: 4277:9c165d0f4899 */ -/* Directory name: t7_reg.txt, Changeset: 5945:1487219ecb20 */ +/* Directory name: t7_sw_reg.txt, Changeset: 5946:0b60ff298e7d */ #define MYPF_BASE 0x1b000 #define MYPF_REG(reg_addr) (MYPF_BASE + (reg_addr)) @@ -44006,10 +44006,57 @@ #define V_MPS2CRYPTO_RX_INTF_FIFO(x) ((x) << S_MPS2CRYPTO_RX_INTF_FIFO) #define G_MPS2CRYPTO_RX_INTF_FIFO(x) (((x) >> S_MPS2CRYPTO_RX_INTF_FIFO) & M_MPS2CRYPTO_RX_INTF_FIFO) -#define S_RX_PRE_PROC_PERR 9 -#define M_RX_PRE_PROC_PERR 0x7ffU -#define V_RX_PRE_PROC_PERR(x) ((x) << S_RX_PRE_PROC_PERR) -#define G_RX_PRE_PROC_PERR(x) (((x) >> S_RX_PRE_PROC_PERR) & M_RX_PRE_PROC_PERR) +#define S_MAC_RX_PPROC_MPS2TP_TF 19 +#define V_MAC_RX_PPROC_MPS2TP_TF(x) ((x) << S_MAC_RX_PPROC_MPS2TP_TF) +#define F_MAC_RX_PPROC_MPS2TP_TF V_MAC_RX_PPROC_MPS2TP_TF(1U) + +#define S_MAC_RX_PPROC_LB_CH3 18 +#define V_MAC_RX_PPROC_LB_CH3(x) ((x) << S_MAC_RX_PPROC_LB_CH3) +#define F_MAC_RX_PPROC_LB_CH3 V_MAC_RX_PPROC_LB_CH3(1U) + +#define S_MAC_RX_PPROC_LB_CH2 17 +#define V_MAC_RX_PPROC_LB_CH2(x) ((x) << S_MAC_RX_PPROC_LB_CH2) +#define F_MAC_RX_PPROC_LB_CH2 V_MAC_RX_PPROC_LB_CH2(1U) + +#define S_MAC_RX_PPROC_LB_CH1 16 +#define V_MAC_RX_PPROC_LB_CH1(x) ((x) << S_MAC_RX_PPROC_LB_CH1) +#define F_MAC_RX_PPROC_LB_CH1 V_MAC_RX_PPROC_LB_CH1(1U) + +#define S_MAC_RX_PPROC_LB_CH0 15 +#define V_MAC_RX_PPROC_LB_CH0(x) ((x) << S_MAC_RX_PPROC_LB_CH0) +#define F_MAC_RX_PPROC_LB_CH0 V_MAC_RX_PPROC_LB_CH0(1U) + +#define S_MAC_RX_PPROC_DWRR_CH0_3 14 +#define V_MAC_RX_PPROC_DWRR_CH0_3(x) ((x) << S_MAC_RX_PPROC_DWRR_CH0_3) +#define F_MAC_RX_PPROC_DWRR_CH0_3 V_MAC_RX_PPROC_DWRR_CH0_3(1U) + +#define S_MAC_RX_FIFO_PERR 13 +#define V_MAC_RX_FIFO_PERR(x) ((x) << S_MAC_RX_FIFO_PERR) +#define F_MAC_RX_FIFO_PERR V_MAC_RX_FIFO_PERR(1U) + +#define S_MAC2MPS_PT3_PERR 12 +#define V_MAC2MPS_PT3_PERR(x) ((x) << S_MAC2MPS_PT3_PERR) +#define F_MAC2MPS_PT3_PERR V_MAC2MPS_PT3_PERR(1U) + +#define S_MAC2MPS_PT2_PERR 11 +#define V_MAC2MPS_PT2_PERR(x) ((x) << S_MAC2MPS_PT2_PERR) +#define F_MAC2MPS_PT2_PERR V_MAC2MPS_PT2_PERR(1U) + +#define S_MAC2MPS_PT1_PERR 10 +#define V_MAC2MPS_PT1_PERR(x) ((x) << S_MAC2MPS_PT1_PERR) +#define F_MAC2MPS_PT1_PERR V_MAC2MPS_PT1_PERR(1U) + +#define S_MAC2MPS_PT0_PERR 9 +#define V_MAC2MPS_PT0_PERR(x) ((x) << S_MAC2MPS_PT0_PERR) +#define F_MAC2MPS_PT0_PERR V_MAC2MPS_PT0_PERR(1U) + +#define S_LPBK_FIFO_PERR 8 +#define V_LPBK_FIFO_PERR(x) ((x) << S_LPBK_FIFO_PERR) +#define F_LPBK_FIFO_PERR V_LPBK_FIFO_PERR(1U) + +#define S_TP2MPS_TF_FIFO_PERR 7 +#define V_TP2MPS_TF_FIFO_PERR(x) ((x) << S_TP2MPS_TF_FIFO_PERR) +#define F_TP2MPS_TF_FIFO_PERR V_TP2MPS_TF_FIFO_PERR(1U) #define A_MPS_RX_PAUSE_GEN_TH_1 0x11090 #define A_MPS_RX_PERR_INT_ENABLE2 0x11090 @@ -78258,6 +78305,26 @@ #define G_RX_CDR_LANE_SEL(x) (((x) >> S_RX_CDR_LANE_SEL) & M_RX_CDR_LANE_SEL) #define A_MAC_DEBUG_PL_IF_1 0x381c4 +#define A_MAC_HSS0_ANALOG_TEST_CTRL 0x381d0 + +#define S_WP_PMT_IN_I 0 +#define M_WP_PMT_IN_I 0xfU +#define V_WP_PMT_IN_I(x) ((x) << S_WP_PMT_IN_I) +#define G_WP_PMT_IN_I(x) (((x) >> S_WP_PMT_IN_I) & M_WP_PMT_IN_I) + +#define A_MAC_HSS1_ANALOG_TEST_CTRL 0x381d4 +#define A_MAC_HSS2_ANALOG_TEST_CTRL 0x381d8 +#define A_MAC_HSS3_ANALOG_TEST_CTRL 0x381dc +#define A_MAC_HSS0_ANALOG_TEST_STATUS 0x381e0 + +#define S_WP_PMT_OUT_O 0 +#define M_WP_PMT_OUT_O 0xfU +#define V_WP_PMT_OUT_O(x) ((x) << S_WP_PMT_OUT_O) +#define G_WP_PMT_OUT_O(x) (((x) >> S_WP_PMT_OUT_O) & M_WP_PMT_OUT_O) + +#define A_MAC_HSS1_ANALOG_TEST_STATUS 0x381e4 +#define A_MAC_HSS2_ANALOG_TEST_STATUS 0x381e8 +#define A_MAC_HSS3_ANALOG_TEST_STATUS 0x381ec #define A_MAC_SIGNAL_DETECT_CTRL 0x381f0 #define S_SIGNAL_DET_LN7 15 @@ -80933,6 +81000,27 @@ #define F_Q1_LOS_0_ASSERT V_Q1_LOS_0_ASSERT(1U) #define A_MAC_IOS_INTR_CAUSE_QUAD1 0x3a09c +#define A_MAC_HSS0_PMD_RECEIVE_SIGNAL_DETECT 0x3a93c + +#define S_PMD_RECEIVE_SIGNAL_DETECT_1N3 4 +#define V_PMD_RECEIVE_SIGNAL_DETECT_1N3(x) ((x) << S_PMD_RECEIVE_SIGNAL_DETECT_1N3) +#define F_PMD_RECEIVE_SIGNAL_DETECT_1N3 V_PMD_RECEIVE_SIGNAL_DETECT_1N3(1U) + +#define S_PMD_RECEIVE_SIGNAL_DETECT_1N2 3 +#define V_PMD_RECEIVE_SIGNAL_DETECT_1N2(x) ((x) << S_PMD_RECEIVE_SIGNAL_DETECT_1N2) +#define F_PMD_RECEIVE_SIGNAL_DETECT_1N2 V_PMD_RECEIVE_SIGNAL_DETECT_1N2(1U) + +#define S_PMD_RECEIVE_SIGNAL_DETECT_LN1 2 +#define V_PMD_RECEIVE_SIGNAL_DETECT_LN1(x) ((x) << S_PMD_RECEIVE_SIGNAL_DETECT_LN1) +#define F_PMD_RECEIVE_SIGNAL_DETECT_LN1 V_PMD_RECEIVE_SIGNAL_DETECT_LN1(1U) + +#define S_PMD_RECEIVE_SIGNAL_DETECT_1N0 1 +#define V_PMD_RECEIVE_SIGNAL_DETECT_1N0(x) ((x) << S_PMD_RECEIVE_SIGNAL_DETECT_1N0) +#define F_PMD_RECEIVE_SIGNAL_DETECT_1N0 V_PMD_RECEIVE_SIGNAL_DETECT_1N0(1U) + +#define A_MAC_HSS1_PMD_RECEIVE_SIGNAL_DETECT 0x3b93c +#define A_MAC_HSS2_PMD_RECEIVE_SIGNAL_DETECT 0x3c93c +#define A_MAC_HSS3_PMD_RECEIVE_SIGNAL_DETECT 0x3d93c #define A_MAC_MTIP_PCS_1G_0_CONTROL 0x3e000 #define S_SPEED_SEL_1 13 diff --git a/sys/dev/cxgbe/crypto/t7_kern_tls.c b/sys/dev/cxgbe/crypto/t7_kern_tls.c index 217459126361..d9710b5bd13f 100644 --- a/sys/dev/cxgbe/crypto/t7_kern_tls.c +++ b/sys/dev/cxgbe/crypto/t7_kern_tls.c @@ -141,7 +141,8 @@ alloc_tlspcb(struct ifnet *ifp, struct vi_info *vi, int flags) tlsp->tx_key_addr = -1; tlsp->ghash_offset = -1; tlsp->rx_chid = pi->rx_chan; - tlsp->rx_qid = sc->sge.rxq[pi->vi->first_rxq].iq.abs_id; + tlsp->rx_qid = -1; + tlsp->txq = NULL; mbufq_init(&tlsp->pending_mbufs, INT_MAX); return (tlsp); @@ -157,7 +158,8 @@ t7_tls_tag_alloc(struct ifnet *ifp, union if_snd_tag_alloc_params *params, struct vi_info *vi; struct inpcb *inp; struct sge_txq *txq; - int error, iv_size, keyid, mac_first; + int error, iv_size, keyid, mac_first, qidx; + uint32_t flowid; tls = params->tls.tls; @@ -250,11 +252,15 @@ t7_tls_tag_alloc(struct ifnet *ifp, union if_snd_tag_alloc_params *params, goto failed; } - txq = &sc->sge.txq[vi->first_txq]; if (inp->inp_flowtype != M_HASHTYPE_NONE) - txq += ((inp->inp_flowid % (vi->ntxq - vi->rsrv_noflowq)) + - vi->rsrv_noflowq); - tlsp->txq = txq; + flowid = inp->inp_flowid; + else + flowid = arc4random(); + qidx = flowid % vi->nrxq + vi->first_rxq; + tlsp->rx_qid = sc->sge.rxq[qidx].iq.abs_id; + qidx = (flowid % (vi->ntxq - vi->rsrv_noflowq)) + vi->rsrv_noflowq + + vi->first_txq; + tlsp->txq = txq = &sc->sge.txq[qidx]; INP_RUNLOCK(inp); error = ktls_setup_keys(tlsp, tls, txq); diff --git a/sys/dev/cxgbe/firmware/t4fw_interface.h b/sys/dev/cxgbe/firmware/t4fw_interface.h index 5874f0343b03..b11552dce021 100644 --- a/sys/dev/cxgbe/firmware/t4fw_interface.h +++ b/sys/dev/cxgbe/firmware/t4fw_interface.h @@ -8967,9 +8967,10 @@ enum fw_port_type { FW_PORT_TYPE_SFP28 = 20, /* No, 1, 25G/10G/1G */ FW_PORT_TYPE_KR_SFP28 = 21, /* No, 1, 25G/10G/1G using Backplane */ FW_PORT_TYPE_KR_XLAUI = 22, /* No, 4, 40G/10G/1G, No AN*/ - FW_PORT_TYPE_SFP56 = 26, - FW_PORT_TYPE_QSFP56 = 27, - FW_PORT_TYPE_NONE = M_FW_PORT_CMD_PTYPE + FW_PORT_TYPE_SFP56 = 26, /* No, 1, 50G/25G */ + FW_PORT_TYPE_QSFP56 = 27, /* No, 4, 200G/100G/50G/25G */ + FW_PORT_TYPE_QSFPDD = 34, /* No, 8, 400G/200G/100G/50G */ + FW_PORT_TYPE_NONE = M_FW_PORT_CMD_PORTTYPE32 }; static inline bool diff --git a/sys/dev/cxgbe/nvmf/nvmf_che.c b/sys/dev/cxgbe/nvmf/nvmf_che.c new file mode 100644 index 000000000000..5c2174b8a40b --- /dev/null +++ b/sys/dev/cxgbe/nvmf/nvmf_che.c @@ -0,0 +1,3331 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Chelsio Communications, Inc. + * Written by: John Baldwin <jhb@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_inet.h" + +#include <sys/param.h> +#include <sys/libkern.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#ifdef TCP_OFFLOAD +#include <sys/bitset.h> +#include <sys/capsicum.h> +#include <sys/file.h> +#include <sys/kthread.h> +#include <sys/ktr.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/nv.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/tcp_var.h> +#include <netinet/toecore.h> + +#include <dev/nvmf/nvmf.h> +#include <dev/nvmf/nvmf_proto.h> +#include <dev/nvmf/nvmf_tcp.h> +#include <dev/nvmf/nvmf_transport.h> +#include <dev/nvmf/nvmf_transport_internal.h> + +#include <vm/pmap.h> +#include <vm/vm_page.h> + +#include "common/common.h" +#include "common/t4_regs.h" +#include "common/t4_tcb.h" +#include "tom/t4_tom.h" + +/* Status code values in CPL_NVMT_CMP. */ +#define CMP_STATUS_ERROR_MASK 0x7f +#define CMP_STATUS_NO_ERROR 0 +#define CMP_STATUS_HEADER_DIGEST 1 +#define CMP_STATUS_DIRECTION_MISMATCH 2 +#define CMP_STATUS_DIGEST_FLAG_MISMATCH 3 +#define CMP_STATUS_SUCCESS_NOT_LAST 4 +#define CMP_STATUS_BAD_DATA_LENGTH 5 +#define CMP_STATUS_USER_MODE_UNALLOCATED 6 +#define CMP_STATUS_RQT_LIMIT 7 +#define CMP_STATUS_RQT_WRAP 8 +#define CMP_STATUS_RQT_BOUND 9 +#define CMP_STATUS_TPT_LIMIT 16 +#define CMP_STATUS_TPT_INVALID 17 +#define CMP_STATUS_TPT_COLOUR_MISMATCH 18 +#define CMP_STATUS_TPT_MISC 19 +#define CMP_STATUS_TPT_WRAP 20 +#define CMP_STATUS_TPT_BOUND 21 +#define CMP_STATUS_TPT_LAST_PDU_UNALIGNED 22 +#define CMP_STATUS_PBL_LIMIT 24 +#define CMP_STATUS_DATA_DIGEST 25 +#define CMP_STATUS_DDP 0x80 + +/* + * Transfer tags and CIDs with the MSB set are "unallocated" tags that + * pass data through to the freelist without using DDP. + */ +#define CHE_FL_TAG_MASK 0x8000 +#define CHE_MAX_FL_TAG 0x7fff +#define CHE_NUM_FL_TAGS (CHE_MAX_FL_TAG + 1) + +#define CHE_TAG_IS_FL(ttag) (((ttag) & CHE_FL_TAG_MASK) == CHE_FL_TAG_MASK) +#define CHE_RAW_FL_TAG(ttag) ((ttag) & ~CHE_FL_TAG_MASK) +#define CHE_DDP_TAG(stag_idx, color) ((stag_idx) << 4 | (color)) +#define CHE_STAG_COLOR(stag) ((stag) & 0xf) +#define CHE_STAG_IDX(stag) ((stag) >> 4) +#define CHE_DDP_MAX_COLOR 0xf + +#define CHE_DDP_NO_TAG 0xffff + +/* + * A bitmap of non-DDP CIDs in use on the host. Since there is no + * _BIT_FFC (find first clear), the bitset is inverted so that a clear + * bit indicates an in-use CID. + */ +BITSET_DEFINE(fl_cid_set, CHE_NUM_FL_TAGS); +#define FL_CID_INIT(p) __BIT_FILL(CHE_NUM_FL_TAGS, p) +#define FL_CID_BUSY(n, p) __BIT_CLR(CHE_NUM_FL_TAGS, n, p) +#define FL_CID_ISACTIVE(n, p) !__BIT_ISSET(CHE_NUM_FL_TAGS, n, p) +#define FL_CID_FREE(n, p) __BIT_SET(CHE_NUM_FL_TAGS, n, p) +#define FL_CID_FINDFREE_AT(p, start) __BIT_FFS_AT(CHE_NUM_FL_TAGS, p, start) + +/* + * The TCP sequence number of both CPL_NVMT_DATA and CPL_NVMT_CMP + * mbufs are saved here while the mbuf is in qp->rx_data and qp->rx_pdus. + */ +#define nvmf_tcp_seq PH_loc.thirtytwo[0] + +/* + * The CPL status of CPL_NVMT_CMP mbufs are saved here while the mbuf + * is in qp->rx_pdus. + */ +#define nvmf_cpl_status PH_loc.eight[4] + +struct nvmf_che_capsule; +struct nvmf_che_qpair; + +struct nvmf_che_adapter { + struct adapter *sc; + + u_int ddp_threshold; + u_int max_transmit_pdu; + u_int max_receive_pdu; + bool nvmt_data_iqe; + + struct sysctl_ctx_list ctx; /* from uld_activate to deactivate */ +}; + +struct nvmf_che_command_buffer { + struct nvmf_che_qpair *qp; + + struct nvmf_io_request io; + size_t data_len; + size_t data_xfered; + uint32_t data_offset; + + u_int refs; + int error; + + bool ddp_ok; + uint16_t cid; + uint16_t ttag; + uint16_t original_cid; /* Host only */ + + TAILQ_ENTRY(nvmf_che_command_buffer) link; + + /* Fields used for DDP. */ + struct fw_ri_tpte tpte; + uint64_t *pbl; + uint32_t pbl_addr; + uint32_t pbl_len; + + /* Controller only */ + struct nvmf_che_capsule *cc; +}; + +struct nvmf_che_command_buffer_list { + TAILQ_HEAD(, nvmf_che_command_buffer) head; + struct mtx lock; +}; + +struct nvmf_che_qpair { + struct nvmf_qpair qp; + + struct socket *so; + struct toepcb *toep; + struct nvmf_che_adapter *nca; + + volatile u_int refs; /* Every allocated capsule holds a reference */ + uint8_t txpda; + uint8_t rxpda; + bool header_digests; + bool data_digests; + uint32_t maxr2t; + uint32_t maxh2cdata; /* Controller only */ + uint32_t max_rx_data; + uint32_t max_tx_data; + uint32_t max_icd; /* Host only */ + uint32_t max_ioccsz; /* Controller only */ + union { + uint16_t next_fl_ttag; /* Controller only */ + uint16_t next_cid; /* Host only */ + }; + uint16_t next_ddp_tag; + u_int num_fl_ttags; /* Controller only */ + u_int active_fl_ttags; /* Controller only */ + u_int num_ddp_tags; + u_int active_ddp_tags; + bool send_success; /* Controller only */ + uint8_t ddp_color; + uint32_t tpt_offset; + + /* Receive state. */ + struct thread *rx_thread; + struct cv rx_cv; + bool rx_shutdown; + int rx_error; + struct mbufq rx_data; /* Data received via CPL_NVMT_DATA. */ + struct mbufq rx_pdus; /* PDU headers received via CPL_NVMT_CMP. */ + + /* Transmit state. */ + struct thread *tx_thread; + struct cv tx_cv; + bool tx_shutdown; + STAILQ_HEAD(, nvmf_che_capsule) tx_capsules; + + struct nvmf_che_command_buffer_list tx_buffers; + struct nvmf_che_command_buffer_list rx_buffers; + + /* + * For the controller, an RX command buffer can be in one of + * three locations, all protected by the rx_buffers.lock. If + * a receive request is waiting for either an R2T slot for its + * command (due to exceeding MAXR2T), or a transfer tag it is + * placed on the rx_buffers list. When a request is allocated + * an active transfer tag, it moves to either the + * open_ddp_tags[] or open_fl_ttags[] array (indexed by the + * tag) until it completes. + * + * For the host, an RX command buffer using DDP is in + * open_ddp_tags[], otherwise it is in rx_buffers. + */ + struct nvmf_che_command_buffer **open_ddp_tags; + struct nvmf_che_command_buffer **open_fl_ttags; /* Controller only */ + + /* + * For the host, CIDs submitted by nvmf(4) must be rewritten + * to either use DDP or not use DDP. The CID in response + * capsules must be restored to their original value. For + * DDP, the original CID is stored in the command buffer. + * These variables manage non-DDP CIDs. + */ + uint16_t *fl_cids; /* Host only */ + struct fl_cid_set *fl_cid_set; /* Host only */ + struct mtx fl_cid_lock; /* Host only */ +}; + +struct nvmf_che_rxpdu { + struct mbuf *m; + const struct nvme_tcp_common_pdu_hdr *hdr; + uint32_t data_len; + bool data_digest_mismatch; + bool ddp; +}; + +struct nvmf_che_capsule { + struct nvmf_capsule nc; + + volatile u_int refs; + + struct nvmf_che_rxpdu rx_pdu; + + uint32_t active_r2ts; /* Controller only */ +#ifdef INVARIANTS + uint32_t tx_data_offset; /* Controller only */ + u_int pending_r2ts; /* Controller only */ +#endif + + STAILQ_ENTRY(nvmf_che_capsule) link; +}; + +#define CCAP(nc) ((struct nvmf_che_capsule *)(nc)) +#define CQP(qp) ((struct nvmf_che_qpair *)(qp)) + +static void che_release_capsule(struct nvmf_che_capsule *cc); +static void che_free_qpair(struct nvmf_qpair *nq); + +SYSCTL_NODE(_kern_nvmf, OID_AUTO, che, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, + "Chelsio TCP offload transport"); + +static u_int che_max_transmit_pdu = 32 * 1024; +SYSCTL_UINT(_kern_nvmf_che, OID_AUTO, max_transmit_pdu, CTLFLAG_RWTUN, + &che_max_transmit_pdu, 0, + "Maximum size of a transmitted PDU"); + +static u_int che_max_receive_pdu = 32 * 1024; +SYSCTL_UINT(_kern_nvmf_che, OID_AUTO, max_receive_pdu, CTLFLAG_RWTUN, + &che_max_receive_pdu, 0, + "Maximum size of a received PDU"); + +static int use_dsgl = 1; +SYSCTL_INT(_kern_nvmf_che, OID_AUTO, use_dsgl, CTLFLAG_RWTUN, &use_dsgl, 0, + "Use DSGL for PBL/FastReg (default=1)"); + +static int inline_threshold = 256; +SYSCTL_INT(_kern_nvmf_che, OID_AUTO, inline_threshold, CTLFLAG_RWTUN, + &inline_threshold, 0, + "inline vs dsgl threshold (default=256)"); + +static int ddp_tags_per_qp = 128; +SYSCTL_INT(_kern_nvmf_che, OID_AUTO, ddp_tags_per_qp, CTLFLAG_RWTUN, + &ddp_tags_per_qp, 0, + "Number of DDP tags to reserve for each queue pair"); + +static MALLOC_DEFINE(M_NVMF_CHE, "nvmf_che", "Chelsio NVMe-TCP offload"); + +/* + * PBL regions consist of N full-sized pages. TPT entries support an + * initial offset into the first page (FBO) and can handle a partial + * length on the last page. + */ +static bool +che_ddp_io_check(struct nvmf_che_qpair *qp, const struct nvmf_io_request *io) +{ + const struct memdesc *mem = &io->io_mem; + struct bus_dma_segment *ds; + int i; + + if (io->io_len < qp->nca->ddp_threshold) { + return (false); + } + + switch (mem->md_type) { + case MEMDESC_VADDR: + case MEMDESC_PADDR: + case MEMDESC_VMPAGES: + return (true); + case MEMDESC_VLIST: + case MEMDESC_PLIST: + /* + * Require all but the first segment to start on a + * page boundary. Require all but the last segment to + * end on a page boundary. + */ + ds = mem->u.md_list; + for (i = 0; i < mem->md_nseg; i++, ds++) { + if (i != 0 && ds->ds_addr % PAGE_SIZE != 0) + return (false); + if (i != mem->md_nseg - 1 && + (ds->ds_addr + ds->ds_len) % PAGE_SIZE != 0) + return (false); + } + return (true); + default: + /* + * Other types could be validated with more work, but + * they aren't used currently by nvmf(4) or nvmft(4). + */ + return (false); + } +} + +static u_int +che_fbo(struct nvmf_che_command_buffer *cb) +{ + struct memdesc *mem = &cb->io.io_mem; + + switch (mem->md_type) { + case MEMDESC_VADDR: + return ((uintptr_t)mem->u.md_vaddr & PAGE_MASK); + case MEMDESC_PADDR: + return (mem->u.md_paddr & PAGE_MASK); + case MEMDESC_VMPAGES: + return (mem->md_offset); + case MEMDESC_VLIST: + case MEMDESC_PLIST: + return (mem->u.md_list[0].ds_addr & PAGE_MASK); + default: + __assert_unreachable(); + } +} + +static u_int +che_npages(struct nvmf_che_command_buffer *cb) +{ + return (howmany(che_fbo(cb) + cb->io.io_len, PAGE_SIZE)); +} + +static struct nvmf_che_command_buffer * +che_alloc_command_buffer(struct nvmf_che_qpair *qp, + const struct nvmf_io_request *io, uint32_t data_offset, size_t data_len, + uint16_t cid) +{ + struct nvmf_che_command_buffer *cb; + + cb = malloc(sizeof(*cb), M_NVMF_CHE, M_WAITOK); + cb->qp = qp; + cb->io = *io; + cb->data_offset = data_offset; + cb->data_len = data_len; + cb->data_xfered = 0; + refcount_init(&cb->refs, 1); + cb->error = 0; + cb->ddp_ok = che_ddp_io_check(qp, io); + cb->cid = cid; + cb->ttag = 0; + cb->original_cid = 0; + cb->cc = NULL; + cb->pbl = NULL; + + return (cb); +} + +static void +che_hold_command_buffer(struct nvmf_che_command_buffer *cb) +{ + refcount_acquire(&cb->refs); +} + +static void +che_free_command_buffer(struct nvmf_che_command_buffer *cb) +{ + nvmf_complete_io_request(&cb->io, cb->data_xfered, cb->error); + if (cb->cc != NULL) + che_release_capsule(cb->cc); + MPASS(cb->pbl == NULL); + free(cb, M_NVMF_CHE); +} + +static void +che_release_command_buffer(struct nvmf_che_command_buffer *cb) +{ + if (refcount_release(&cb->refs)) + che_free_command_buffer(cb); +} + +static void +che_add_command_buffer(struct nvmf_che_command_buffer_list *list, + struct nvmf_che_command_buffer *cb) +{ + mtx_assert(&list->lock, MA_OWNED); + TAILQ_INSERT_HEAD(&list->head, cb, link); +} + +static struct nvmf_che_command_buffer * +che_find_command_buffer(struct nvmf_che_command_buffer_list *list, + uint16_t cid) +{ + struct nvmf_che_command_buffer *cb; + + mtx_assert(&list->lock, MA_OWNED); + TAILQ_FOREACH(cb, &list->head, link) { + if (cb->cid == cid) + return (cb); + } + return (NULL); +} + +static void +che_remove_command_buffer(struct nvmf_che_command_buffer_list *list, + struct nvmf_che_command_buffer *cb) +{ + mtx_assert(&list->lock, MA_OWNED); + TAILQ_REMOVE(&list->head, cb, link); +} + +static void +che_purge_command_buffer(struct nvmf_che_command_buffer_list *list, + uint16_t cid) +{ + struct nvmf_che_command_buffer *cb; + + mtx_lock(&list->lock); + cb = che_find_command_buffer(list, cid); + if (cb != NULL) { + che_remove_command_buffer(list, cb); + mtx_unlock(&list->lock); + che_release_command_buffer(cb); + } else + mtx_unlock(&list->lock); +} + +static int +che_write_mem_inline(struct adapter *sc, struct toepcb *toep, uint32_t addr, + uint32_t len, void *data, struct mbufq *wrq) +{ + struct mbuf *m; + char *cp; + int copy_len, i, num_wqe, wr_len; + +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: addr 0x%x len %u", __func__, addr << 5, len); +#endif + num_wqe = DIV_ROUND_UP(len, T4_MAX_INLINE_SIZE); + cp = data; + for (i = 0; i < num_wqe; i++) { + copy_len = min(len, T4_MAX_INLINE_SIZE); + wr_len = T4_WRITE_MEM_INLINE_LEN(copy_len); + + m = alloc_raw_wr_mbuf(wr_len); + if (m == NULL) + return (ENOMEM); + t4_write_mem_inline_wr(sc, mtod(m, void *), wr_len, toep->tid, + addr, copy_len, cp, 0); + if (cp != NULL) + cp += T4_MAX_INLINE_SIZE; + addr += T4_MAX_INLINE_SIZE >> 5; + len -= T4_MAX_INLINE_SIZE; + + mbufq_enqueue(wrq, m); + } + return (0); +} + +static int +che_write_mem_dma_aligned(struct adapter *sc, struct toepcb *toep, + uint32_t addr, uint32_t len, void *data, struct mbufq *wrq) +{ + struct mbuf *m; + vm_offset_t va; + u_int todo; + int wr_len; + + /* First page. */ + va = (vm_offset_t)data; + todo = min(PAGE_SIZE - (va % PAGE_SIZE), len); + wr_len = T4_WRITE_MEM_DMA_LEN; + m = alloc_raw_wr_mbuf(wr_len); + if (m == NULL) + return (ENOMEM); + t4_write_mem_dma_wr(sc, mtod(m, void *), wr_len, toep->tid, addr, + todo, pmap_kextract(va), 0); + mbufq_enqueue(wrq, m); + len -= todo; + addr += todo >> 5; + va += todo; + + while (len > 0) { + MPASS(va == trunc_page(va)); + todo = min(PAGE_SIZE, len); + m = alloc_raw_wr_mbuf(wr_len); + if (m == NULL) + return (ENOMEM); + t4_write_mem_dma_wr(sc, mtod(m, void *), wr_len, toep->tid, + addr, todo, pmap_kextract(va), 0); + mbufq_enqueue(wrq, m); + len -= todo; + addr += todo >> 5; + va += todo; + } + return (0); +} + +static int +che_write_adapter_mem(struct nvmf_che_qpair *qp, uint32_t addr, uint32_t len, + void *data) +{ + struct adapter *sc = qp->nca->sc; + struct toepcb *toep = qp->toep; + struct socket *so = qp->so; + struct inpcb *inp = sotoinpcb(so); + struct mbufq mq; + int error; + + mbufq_init(&mq, INT_MAX); + if (!use_dsgl || len < inline_threshold || data == NULL) + error = che_write_mem_inline(sc, toep, addr, len, data, &mq); + else + error = che_write_mem_dma_aligned(sc, toep, addr, len, data, + &mq); + if (__predict_false(error != 0)) + goto error; + + INP_WLOCK(inp); + if ((inp->inp_flags & INP_DROPPED) != 0) { + INP_WUNLOCK(inp); + error = ECONNRESET; + goto error; + } + mbufq_concat(&toep->ulp_pduq, &mq); + INP_WUNLOCK(inp); + return (0); + +error: + mbufq_drain(&mq); + return (error); +} + +static bool +che_alloc_pbl(struct nvmf_che_qpair *qp, struct nvmf_che_command_buffer *cb) +{ + struct adapter *sc = qp->nca->sc; + struct memdesc *mem = &cb->io.io_mem; + uint64_t *pbl; + uint32_t addr, len; + u_int i, npages; + int error; + + MPASS(cb->pbl == NULL); + MPASS(cb->ddp_ok); + + /* Hardware limit? iWARP only enforces this for T5. */ + if (cb->io.io_len >= (8 * 1024 * 1024 * 1024ULL)) + return (false); + + npages = che_npages(cb); + len = roundup2(npages, 4) * sizeof(*cb->pbl); + addr = t4_pblpool_alloc(sc, len); + if (addr == 0) + return (false); + + pbl = malloc(len, M_NVMF_CHE, M_NOWAIT | M_ZERO); + if (pbl == NULL) { + t4_pblpool_free(sc, addr, len); + return (false); + } + + switch (mem->md_type) { + case MEMDESC_VADDR: + { + vm_offset_t va; + + va = trunc_page((uintptr_t)mem->u.md_vaddr); + for (i = 0; i < npages; i++) + pbl[i] = htobe64(pmap_kextract(va + i * PAGE_SIZE)); + break; + } + case MEMDESC_PADDR: + { + vm_paddr_t pa; + + pa = trunc_page(mem->u.md_paddr); + for (i = 0; i < npages; i++) + pbl[i] = htobe64(pa + i * PAGE_SIZE); + break; + } + case MEMDESC_VMPAGES: + for (i = 0; i < npages; i++) + pbl[i] = htobe64(VM_PAGE_TO_PHYS(mem->u.md_ma[i])); + break; + case MEMDESC_VLIST: + { + struct bus_dma_segment *ds; + vm_offset_t va; + vm_size_t len; + u_int j, k; + + i = 0; + ds = mem->u.md_list; + for (j = 0; j < mem->md_nseg; j++, ds++) { + va = trunc_page((uintptr_t)ds->ds_addr); + len = ds->ds_len; + if (ds->ds_addr % PAGE_SIZE != 0) + len += ds->ds_addr % PAGE_SIZE; + for (k = 0; k < howmany(len, PAGE_SIZE); k++) { + pbl[i] = htobe64(pmap_kextract(va + + k * PAGE_SIZE)); + i++; + } + } + MPASS(i == npages); + break; + } + case MEMDESC_PLIST: + { + struct bus_dma_segment *ds; + vm_paddr_t pa; + vm_size_t len; + u_int j, k; + + i = 0; + ds = mem->u.md_list; + for (j = 0; j < mem->md_nseg; j++, ds++) { + pa = trunc_page((vm_paddr_t)ds->ds_addr); + len = ds->ds_len; + if (ds->ds_addr % PAGE_SIZE != 0) + len += ds->ds_addr % PAGE_SIZE; + for (k = 0; k < howmany(len, PAGE_SIZE); k++) { + pbl[i] = htobe64(pa + k * PAGE_SIZE); + i++; + } + } + MPASS(i == npages); + break; + } + default: + __assert_unreachable(); + } + + error = che_write_adapter_mem(qp, addr >> 5, len, pbl); + if (error != 0) { + t4_pblpool_free(sc, addr, len); + free(pbl, M_NVMF_CHE); + return (false); + } + + cb->pbl = pbl; + cb->pbl_addr = addr; + cb->pbl_len = len; + + return (true); +} + +static void +che_free_pbl(struct nvmf_che_command_buffer *cb) +{ + free(cb->pbl, M_NVMF_CHE); + t4_pblpool_free(cb->qp->nca->sc, cb->pbl_addr, cb->pbl_len); + cb->pbl = NULL; + cb->pbl_addr = 0; + cb->pbl_len = 0; +} + +static bool +che_write_tpt_entry(struct nvmf_che_qpair *qp, + struct nvmf_che_command_buffer *cb, uint16_t stag) +{ + uint32_t tpt_addr; + int error; + + cb->tpte.valid_to_pdid = htobe32(F_FW_RI_TPTE_VALID | + V_FW_RI_TPTE_STAGKEY(CHE_STAG_COLOR(stag)) | + F_FW_RI_TPTE_STAGSTATE | + V_FW_RI_TPTE_STAGTYPE(FW_RI_STAG_NSMR) | + V_FW_RI_TPTE_PDID(0)); + cb->tpte.locread_to_qpid = htobe32( + V_FW_RI_TPTE_PERM(FW_RI_MEM_ACCESS_REM_WRITE) | + V_FW_RI_TPTE_ADDRTYPE(FW_RI_ZERO_BASED_TO) | + V_FW_RI_TPTE_PS(PAGE_SIZE) | + V_FW_RI_TPTE_QPID(qp->toep->tid)); +#define PBL_OFF(qp, a) ((a) - (qp)->nca->sc->vres.pbl.start) + cb->tpte.nosnoop_pbladdr = + htobe32(V_FW_RI_TPTE_PBLADDR(PBL_OFF(qp, cb->pbl_addr) >> 3)); + cb->tpte.len_lo = htobe32(cb->data_len); + cb->tpte.va_hi = 0; + cb->tpte.va_lo_fbo = htobe32(che_fbo(cb)); + cb->tpte.dca_mwbcnt_pstag = 0; + cb->tpte.len_hi = htobe32(cb->data_offset); + + tpt_addr = qp->tpt_offset + CHE_STAG_IDX(stag) + + (qp->nca->sc->vres.stag.start >> 5); + + error = che_write_adapter_mem(qp, tpt_addr, sizeof(cb->tpte), + &cb->tpte); + return (error == 0); +} + +static void +che_clear_tpt_entry(struct nvmf_che_qpair *qp, uint16_t stag) +{ + uint32_t tpt_addr; + + tpt_addr = qp->tpt_offset + CHE_STAG_IDX(stag) + + (qp->nca->sc->vres.stag.start >> 5); + + (void)che_write_adapter_mem(qp, tpt_addr, sizeof(struct fw_ri_tpte), + NULL); +} + +static uint16_t +che_alloc_ddp_stag(struct nvmf_che_qpair *qp, + struct nvmf_che_command_buffer *cb) +{ + uint16_t stag_idx; + + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + MPASS(cb->ddp_ok); + + if (qp->active_ddp_tags == qp->num_ddp_tags) + return (CHE_DDP_NO_TAG); + + MPASS(qp->num_ddp_tags != 0); + + stag_idx = qp->next_ddp_tag; + for (;;) { + if (qp->open_ddp_tags[stag_idx] == NULL) + break; + if (stag_idx == qp->num_ddp_tags - 1) { + stag_idx = 0; + if (qp->ddp_color == CHE_DDP_MAX_COLOR) + qp->ddp_color = 0; + else + qp->ddp_color++; + } else + stag_idx++; + MPASS(stag_idx != qp->next_ddp_tag); + } + if (stag_idx == qp->num_ddp_tags - 1) + qp->next_ddp_tag = 0; + else + qp->next_ddp_tag = stag_idx + 1; + + qp->active_ddp_tags++; + qp->open_ddp_tags[stag_idx] = cb; + + return (CHE_DDP_TAG(stag_idx, qp->ddp_color)); +} + +static void +che_free_ddp_stag(struct nvmf_che_qpair *qp, struct nvmf_che_command_buffer *cb, + uint16_t stag) +{ + MPASS(!CHE_TAG_IS_FL(stag)); + + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + + MPASS(qp->open_ddp_tags[CHE_STAG_IDX(stag)] == cb); + + qp->open_ddp_tags[CHE_STAG_IDX(stag)] = NULL; + qp->active_ddp_tags--; +} + +static uint16_t +che_alloc_ddp_tag(struct nvmf_che_qpair *qp, + struct nvmf_che_command_buffer *cb) +{ + uint16_t stag; + + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + + if (!cb->ddp_ok) + return (CHE_DDP_NO_TAG); + + stag = che_alloc_ddp_stag(qp, cb); + if (stag == CHE_DDP_NO_TAG) { + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_ddp_setup_no_stag, + 1); + return (CHE_DDP_NO_TAG); + } + + if (!che_alloc_pbl(qp, cb)) { + che_free_ddp_stag(qp, cb, stag); + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_ddp_setup_error, 1); + return (CHE_DDP_NO_TAG); + } + + if (!che_write_tpt_entry(qp, cb, stag)) { + che_free_pbl(cb); + che_free_ddp_stag(qp, cb, stag); + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_ddp_setup_error, 1); + return (CHE_DDP_NO_TAG); + } + + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_ddp_setup_ok, 1); + return (stag); +} + +static void +che_free_ddp_tag(struct nvmf_che_qpair *qp, struct nvmf_che_command_buffer *cb, + uint16_t stag) +{ + MPASS(!CHE_TAG_IS_FL(stag)); + + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + + MPASS(qp->open_ddp_tags[CHE_STAG_IDX(stag)] == cb); + + che_clear_tpt_entry(qp, stag); + che_free_pbl(cb); + che_free_ddp_stag(qp, cb, stag); +} + +static void +nvmf_che_write_pdu(struct nvmf_che_qpair *qp, struct mbuf *m) +{ + struct epoch_tracker et; + struct socket *so = qp->so; + struct inpcb *inp = sotoinpcb(so); + struct toepcb *toep = qp->toep; + + CURVNET_SET(so->so_vnet); + NET_EPOCH_ENTER(et); + INP_WLOCK(inp); + if (__predict_false(inp->inp_flags & INP_DROPPED) || + __predict_false((toep->flags & TPF_ATTACHED) == 0)) { + m_freem(m); + } else { + mbufq_enqueue(&toep->ulp_pduq, m); + t4_push_pdus(toep->vi->adapter, toep, 0); + } + INP_WUNLOCK(inp); + NET_EPOCH_EXIT(et); + CURVNET_RESTORE(); +} + +static void +nvmf_che_report_error(struct nvmf_che_qpair *qp, uint16_t fes, uint32_t fei, + struct mbuf *rx_pdu, u_int hlen) +{ + struct nvme_tcp_term_req_hdr *hdr; + struct mbuf *m; + + if (hlen != 0) { + hlen = min(hlen, NVME_TCP_TERM_REQ_ERROR_DATA_MAX_SIZE); + hlen = min(hlen, m_length(rx_pdu, NULL)); + } + + m = m_get2(sizeof(*hdr) + hlen, M_WAITOK, MT_DATA, M_PKTHDR); + m->m_len = sizeof(*hdr) + hlen; + m->m_pkthdr.len = m->m_len; + hdr = mtod(m, void *); + memset(hdr, 0, sizeof(*hdr)); + hdr->common.pdu_type = qp->qp.nq_controller ? + NVME_TCP_PDU_TYPE_C2H_TERM_REQ : NVME_TCP_PDU_TYPE_H2C_TERM_REQ; + hdr->common.hlen = sizeof(*hdr); + hdr->common.plen = sizeof(*hdr) + hlen; + hdr->fes = htole16(fes); + le32enc(hdr->fei, fei); + if (hlen != 0) + m_copydata(rx_pdu, 0, hlen, (caddr_t)(hdr + 1)); + + nvmf_che_write_pdu(qp, m); +} + +static int +nvmf_che_validate_pdu(struct nvmf_che_qpair *qp, struct nvmf_che_rxpdu *pdu) +{ + const struct nvme_tcp_common_pdu_hdr *ch; + struct mbuf *m = pdu->m; + uint32_t data_len, fei, plen, rx_digest; + u_int hlen, cpl_error; + int error; + uint16_t fes; + + /* Determine how large of a PDU header to return for errors. */ + ch = pdu->hdr; + hlen = ch->hlen; + plen = le32toh(ch->plen); + if (hlen < sizeof(*ch) || hlen > plen) + hlen = sizeof(*ch); + + cpl_error = m->m_pkthdr.nvmf_cpl_status & CMP_STATUS_ERROR_MASK; + switch (cpl_error) { + case CMP_STATUS_NO_ERROR: + break; + case CMP_STATUS_HEADER_DIGEST: + counter_u64_add( + qp->toep->ofld_rxq->rx_nvme_header_digest_errors, 1); + printf("NVMe/TCP: Header digest mismatch\n"); + rx_digest = le32dec(mtodo(m, ch->hlen)); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_HDGST_ERROR, rx_digest, m, + hlen); + return (EBADMSG); + case CMP_STATUS_DIRECTION_MISMATCH: + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_invalid_headers, 1); + printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type), m, + hlen); + return (EBADMSG); + case CMP_STATUS_SUCCESS_NOT_LAST: + case CMP_STATUS_DIGEST_FLAG_MISMATCH: + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_invalid_headers, 1); + printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_common_pdu_hdr, flags), m, hlen); + return (EBADMSG); + case CMP_STATUS_BAD_DATA_LENGTH: + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_invalid_headers, 1); + printf("NVMe/TCP: Invalid PDU length %u\n", plen); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_common_pdu_hdr, plen), m, hlen); + return (EBADMSG); + case CMP_STATUS_USER_MODE_UNALLOCATED: + case CMP_STATUS_RQT_LIMIT: + case CMP_STATUS_RQT_WRAP: + case CMP_STATUS_RQT_BOUND: + device_printf(qp->nca->sc->dev, + "received invalid NVMET error %u\n", + cpl_error); + return (ECONNRESET); + case CMP_STATUS_TPT_LIMIT: + case CMP_STATUS_TPT_INVALID: + case CMP_STATUS_TPT_COLOUR_MISMATCH: + case CMP_STATUS_TPT_MISC: + case CMP_STATUS_TPT_WRAP: + case CMP_STATUS_TPT_BOUND: + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_invalid_headers, 1); + switch (ch->pdu_type) { + case NVME_TCP_PDU_TYPE_H2C_DATA: + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_h2c_data_hdr, ttag), + pdu->m, pdu->hdr->hlen); + return (EBADMSG); + case NVME_TCP_PDU_TYPE_C2H_DATA: + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_c2h_data_hdr, cccid), m, + hlen); + return (EBADMSG); + default: + device_printf(qp->nca->sc->dev, + "received DDP NVMET error %u for PDU %u\n", + cpl_error, ch->pdu_type); + return (ECONNRESET); + } + case CMP_STATUS_TPT_LAST_PDU_UNALIGNED: + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_invalid_headers, 1); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR, 0, m, hlen); + return (EBADMSG); + case CMP_STATUS_PBL_LIMIT: + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_invalid_headers, 1); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_OUT_OF_RANGE, 0, m, + hlen); + return (EBADMSG); + case CMP_STATUS_DATA_DIGEST: + /* Handled below. */ + break; + default: + device_printf(qp->nca->sc->dev, + "received unknown NVMET error %u\n", + cpl_error); + return (ECONNRESET); + } + + error = nvmf_tcp_validate_pdu_header(ch, qp->qp.nq_controller, + qp->header_digests, qp->data_digests, qp->rxpda, &data_len, &fes, + &fei); + if (error != 0) { + if (error != ECONNRESET) + nvmf_che_report_error(qp, fes, fei, m, hlen); + return (error); + } + + /* Check data digest if present. */ + pdu->data_digest_mismatch = false; + if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0) { + if (cpl_error == CMP_STATUS_DATA_DIGEST) { + printf("NVMe/TCP: Data digest mismatch\n"); + pdu->data_digest_mismatch = true; + counter_u64_add( + qp->toep->ofld_rxq->rx_nvme_data_digest_errors, 1); + } + } + + pdu->data_len = data_len; + + return (0); +} + +static void +nvmf_che_free_pdu(struct nvmf_che_rxpdu *pdu) +{ + m_freem(pdu->m); + pdu->m = NULL; + pdu->hdr = NULL; +} + +static int +nvmf_che_handle_term_req(struct nvmf_che_rxpdu *pdu) +{ + const struct nvme_tcp_term_req_hdr *hdr; + + hdr = (const void *)pdu->hdr; + + printf("NVMe/TCP: Received termination request: fes %#x fei %#x\n", + le16toh(hdr->fes), le32dec(hdr->fei)); + nvmf_che_free_pdu(pdu); + return (ECONNRESET); +} + +static int +nvmf_che_save_command_capsule(struct nvmf_che_qpair *qp, + struct nvmf_che_rxpdu *pdu) +{ + const struct nvme_tcp_cmd *cmd; + struct nvmf_capsule *nc; + struct nvmf_che_capsule *cc; + + cmd = (const void *)pdu->hdr; + + nc = nvmf_allocate_command(&qp->qp, &cmd->ccsqe, M_WAITOK); + + cc = CCAP(nc); + cc->rx_pdu = *pdu; + + nvmf_capsule_received(&qp->qp, nc); + return (0); +} + +static int +nvmf_che_save_response_capsule(struct nvmf_che_qpair *qp, + struct nvmf_che_rxpdu *pdu) +{ + const struct nvme_tcp_rsp *rsp; + struct nvme_completion cpl; + struct nvmf_capsule *nc; + struct nvmf_che_capsule *cc; + uint16_t cid; + + rsp = (const void *)pdu->hdr; + + /* + * Restore the original CID and ensure any command buffers + * associated with this CID have been released. Once the CQE + * has been received, no further transfers to the command + * buffer for the associated CID can occur. + */ + cpl = rsp->rccqe; + cid = le16toh(cpl.cid); + if (CHE_TAG_IS_FL(cid)) { + cid = CHE_RAW_FL_TAG(cid); + mtx_lock(&qp->fl_cid_lock); + MPASS(FL_CID_ISACTIVE(cid, qp->fl_cid_set)); + cpl.cid = qp->fl_cids[cid]; + FL_CID_FREE(cid, qp->fl_cid_set); + mtx_unlock(&qp->fl_cid_lock); + + che_purge_command_buffer(&qp->rx_buffers, rsp->rccqe.cid); + che_purge_command_buffer(&qp->tx_buffers, rsp->rccqe.cid); + } else { + struct nvmf_che_command_buffer *cb; + + mtx_lock(&qp->rx_buffers.lock); + cb = qp->open_ddp_tags[CHE_STAG_IDX(cid)]; + MPASS(cb != NULL); + MPASS(cb->cid == rsp->rccqe.cid); + cpl.cid = cb->original_cid; + che_free_ddp_tag(qp, cb, cid); + mtx_unlock(&qp->rx_buffers.lock); + che_release_command_buffer(cb); + } +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u freed cid 0x%04x for 0x%04x", __func__, + qp->toep->tid, le16toh(rsp->rccqe.cid), cpl.cid); +#endif + + nc = nvmf_allocate_response(&qp->qp, &cpl, M_WAITOK); + + nc->nc_sqhd_valid = true; + cc = CCAP(nc); + cc->rx_pdu = *pdu; + + nvmf_capsule_received(&qp->qp, nc); + return (0); +} + +/* + * Construct a PDU that contains an optional data payload. This + * includes dealing with the length fields in the common header. The + * adapter inserts digests and padding when the PDU is transmitted. + */ +static struct mbuf * +nvmf_che_construct_pdu(struct nvmf_che_qpair *qp, void *hdr, size_t hlen, + struct mbuf *data, uint32_t data_len) +{ + struct nvme_tcp_common_pdu_hdr *ch; + struct mbuf *top; + uint32_t pdo, plen; + uint8_t ulp_submode; + + plen = hlen; + if (qp->header_digests) + plen += sizeof(uint32_t); + if (data_len != 0) { + KASSERT(m_length(data, NULL) == data_len, ("length mismatch")); + pdo = roundup(plen, qp->txpda); + plen = pdo + data_len; + if (qp->data_digests) + plen += sizeof(uint32_t); + } else { + KASSERT(data == NULL, ("payload mbuf with zero length")); + pdo = 0; + } + + top = m_get2(hlen, M_WAITOK, MT_DATA, M_PKTHDR); + top->m_len = hlen; + top->m_pkthdr.len = hlen; + ch = mtod(top, void *); + memcpy(ch, hdr, hlen); + ch->hlen = hlen; + ulp_submode = 0; + if (qp->header_digests) { + ch->flags |= NVME_TCP_CH_FLAGS_HDGSTF; + ulp_submode |= ULP_CRC_HEADER; + } + if (qp->data_digests && data_len != 0) { + ch->flags |= NVME_TCP_CH_FLAGS_DDGSTF; + ulp_submode |= ULP_CRC_DATA; + } + ch->pdo = pdo; + ch->plen = htole32(plen); + set_mbuf_ulp_submode(top, ulp_submode); + + if (data_len != 0) { + top->m_pkthdr.len += data_len; + top->m_next = data; + } + + return (top); +} + +/* Allocate the next free freelist transfer tag. */ +static bool +nvmf_che_allocate_fl_ttag(struct nvmf_che_qpair *qp, + struct nvmf_che_command_buffer *cb) +{ + uint16_t ttag; + + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + + if (qp->active_fl_ttags == qp->num_fl_ttags) + return (false); + + ttag = qp->next_fl_ttag; + for (;;) { + if (qp->open_fl_ttags[ttag] == NULL) + break; + if (ttag == qp->num_fl_ttags - 1) + ttag = 0; + else + ttag++; + MPASS(ttag != qp->next_fl_ttag); + } + if (ttag == qp->num_fl_ttags - 1) + qp->next_fl_ttag = 0; + else + qp->next_fl_ttag = ttag + 1; + + qp->active_fl_ttags++; + qp->open_fl_ttags[ttag] = cb; + + cb->ttag = ttag | CHE_FL_TAG_MASK; + return (true); +} + +/* Attempt to allocate a free transfer tag and assign it to cb. */ +static bool +nvmf_che_allocate_ttag(struct nvmf_che_qpair *qp, + struct nvmf_che_command_buffer *cb) +{ + uint16_t stag; + + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + + stag = che_alloc_ddp_tag(qp, cb); + if (stag == CHE_DDP_NO_TAG) { + if (!nvmf_che_allocate_fl_ttag(qp, cb)) + return (false); + } else { + cb->ttag = stag; + } +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u allocated ttag 0x%04x", __func__, + qp->toep->tid, cb->ttag); +#endif + cb->cc->active_r2ts++; + return (true); +} + +/* Find the next command buffer eligible to schedule for R2T. */ +static struct nvmf_che_command_buffer * +nvmf_che_next_r2t(struct nvmf_che_qpair *qp) +{ + struct nvmf_che_command_buffer *cb; + + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + + TAILQ_FOREACH(cb, &qp->rx_buffers.head, link) { + /* NB: maxr2t is 0's based. */ + if (cb->cc->active_r2ts > qp->maxr2t) + continue; + + if (!nvmf_che_allocate_ttag(qp, cb)) + return (NULL); +#ifdef INVARIANTS + cb->cc->pending_r2ts--; +#endif + TAILQ_REMOVE(&qp->rx_buffers.head, cb, link); + return (cb); + } + return (NULL); +} + +/* NB: cid and is little-endian already. */ +static void +che_send_r2t(struct nvmf_che_qpair *qp, uint16_t cid, uint16_t ttag, + uint32_t data_offset, uint32_t data_len) +{ + struct nvme_tcp_r2t_hdr r2t; + struct mbuf *m; + + memset(&r2t, 0, sizeof(r2t)); + r2t.common.pdu_type = NVME_TCP_PDU_TYPE_R2T; + r2t.cccid = cid; + r2t.ttag = htole16(ttag); + r2t.r2to = htole32(data_offset); + r2t.r2tl = htole32(data_len); + + m = nvmf_che_construct_pdu(qp, &r2t, sizeof(r2t), NULL, 0); + nvmf_che_write_pdu(qp, m); +} + +/* + * Release a transfer tag and schedule another R2T. + * + * NB: This drops the rx_buffers.lock mutex. + */ +static void +nvmf_che_send_next_r2t(struct nvmf_che_qpair *qp, + struct nvmf_che_command_buffer *cb) +{ + struct nvmf_che_command_buffer *ncb; + + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u freed ttag 0x%04x", __func__, qp->toep->tid, + cb->ttag); +#endif + if (CHE_TAG_IS_FL(cb->ttag)) { + uint16_t ttag; + + ttag = CHE_RAW_FL_TAG(cb->ttag); + MPASS(qp->open_fl_ttags[ttag] == cb); + + /* Release this transfer tag. */ + qp->open_fl_ttags[ttag] = NULL; + qp->active_fl_ttags--; + } else + che_free_ddp_tag(qp, cb, cb->ttag); + + cb->cc->active_r2ts--; + + /* Schedule another R2T. */ + ncb = nvmf_che_next_r2t(qp); + mtx_unlock(&qp->rx_buffers.lock); + if (ncb != NULL) + che_send_r2t(qp, ncb->cid, ncb->ttag, ncb->data_offset, + ncb->data_len); +} + +/* + * Copy len bytes starting at offset skip from an mbuf chain into an + * I/O buffer at destination offset io_offset. + */ +static void +mbuf_copyto_io(struct mbuf *m, u_int skip, u_int len, + struct nvmf_io_request *io, u_int io_offset) +{ + u_int todo; + + while (m->m_len <= skip) { + skip -= m->m_len; + m = m->m_next; + } + while (len != 0) { + MPASS((m->m_flags & M_EXTPG) == 0); + + todo = min(m->m_len - skip, len); + memdesc_copyback(&io->io_mem, io_offset, todo, mtodo(m, skip)); + skip = 0; + io_offset += todo; + len -= todo; + m = m->m_next; + } +} + +static int +nvmf_che_handle_h2c_data(struct nvmf_che_qpair *qp, struct nvmf_che_rxpdu *pdu) +{ + const struct nvme_tcp_h2c_data_hdr *h2c; + struct nvmf_che_command_buffer *cb; + uint32_t data_len, data_offset; + uint16_t ttag, fl_ttag; + + h2c = (const void *)pdu->hdr; + if (le32toh(h2c->datal) > qp->maxh2cdata) { + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_LIMIT_EXCEEDED, 0, + pdu->m, pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + ttag = le16toh(h2c->ttag); + if (CHE_TAG_IS_FL(ttag)) { + fl_ttag = CHE_RAW_FL_TAG(ttag); + if (fl_ttag >= qp->num_fl_ttags) { + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_h2c_data_hdr, ttag), + pdu->m, pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + mtx_lock(&qp->rx_buffers.lock); + cb = qp->open_fl_ttags[fl_ttag]; + } else { + if (CHE_STAG_IDX(ttag) >= qp->num_ddp_tags) { + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_h2c_data_hdr, ttag), + pdu->m, pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + mtx_lock(&qp->rx_buffers.lock); + cb = qp->open_ddp_tags[CHE_STAG_IDX(ttag)]; + } + + if (cb == NULL) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_h2c_data_hdr, ttag), pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + MPASS(cb->ttag == ttag); + + /* For a data digest mismatch, fail the I/O request. */ + if (pdu->data_digest_mismatch) { + nvmf_che_send_next_r2t(qp, cb); + cb->error = EINTEGRITY; + che_release_command_buffer(cb); + nvmf_che_free_pdu(pdu); + return (0); + } + + data_len = le32toh(h2c->datal); + if (data_len != pdu->data_len) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_h2c_data_hdr, datal), pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + data_offset = le32toh(h2c->datao); + if (data_offset < cb->data_offset || + data_offset + data_len > cb->data_offset + cb->data_len) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_OUT_OF_RANGE, 0, pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + if (data_offset != cb->data_offset + cb->data_xfered) { + if (CHE_TAG_IS_FL(ttag)) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR, 0, pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } else { + uint32_t ddp_bytes; + + /* Account for PDUs silently received via DDP. */ + ddp_bytes = data_offset - + (cb->data_offset + cb->data_xfered); + cb->data_xfered += ddp_bytes; +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u previous ddp_bytes %u", + __func__, qp->toep->tid, ddp_bytes); +#endif + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_ddp_octets, + ddp_bytes); + } + } + + if ((cb->data_xfered + data_len == cb->data_len) != + ((pdu->hdr->flags & NVME_TCP_H2C_DATA_FLAGS_LAST_PDU) != 0)) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR, 0, pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + cb->data_xfered += data_len; + data_offset -= cb->data_offset; + if (cb->data_xfered == cb->data_len) { + nvmf_che_send_next_r2t(qp, cb); + } else { + che_hold_command_buffer(cb); + mtx_unlock(&qp->rx_buffers.lock); + } + + if (CHE_TAG_IS_FL(ttag)) + mbuf_copyto_io(pdu->m->m_next, 0, data_len, &cb->io, + data_offset); + + che_release_command_buffer(cb); + nvmf_che_free_pdu(pdu); + return (0); +} + +static int +nvmf_che_handle_c2h_data(struct nvmf_che_qpair *qp, struct nvmf_che_rxpdu *pdu) +{ + const struct nvme_tcp_c2h_data_hdr *c2h; + struct nvmf_che_command_buffer *cb; + uint32_t data_len, data_offset; + uint16_t cid, original_cid; + + /* + * Unlike freelist command buffers, DDP command buffers are + * not released until the response capsule is received to keep + * the STAG allocated until the command has completed. + */ + c2h = (const void *)pdu->hdr; + + cid = le16toh(c2h->cccid); + if (CHE_TAG_IS_FL(cid)) { + mtx_lock(&qp->rx_buffers.lock); + cb = che_find_command_buffer(&qp->rx_buffers, c2h->cccid); + } else { + if (CHE_STAG_IDX(cid) >= qp->num_ddp_tags) { + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_c2h_data_hdr, cccid), + pdu->m, pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + mtx_lock(&qp->rx_buffers.lock); + cb = qp->open_ddp_tags[CHE_STAG_IDX(cid)]; + } + + if (cb == NULL) { + mtx_unlock(&qp->rx_buffers.lock); + /* + * XXX: Could be PDU sequence error if cccid is for a + * command that doesn't use a command buffer. + */ + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_c2h_data_hdr, cccid), pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + /* For a data digest mismatch, fail the I/O request. */ + if (pdu->data_digest_mismatch) { + cb->error = EINTEGRITY; + if (CHE_TAG_IS_FL(cid)) { + che_remove_command_buffer(&qp->rx_buffers, cb); + mtx_unlock(&qp->rx_buffers.lock); + che_release_command_buffer(cb); + } else + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_free_pdu(pdu); + return (0); + } + + data_len = le32toh(c2h->datal); + if (data_len != pdu->data_len) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_c2h_data_hdr, datal), pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + data_offset = le32toh(c2h->datao); + if (data_offset < cb->data_offset || + data_offset + data_len > cb->data_offset + cb->data_len) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_OUT_OF_RANGE, 0, + pdu->m, pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + if (data_offset != cb->data_offset + cb->data_xfered) { + if (CHE_TAG_IS_FL(cid)) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR, 0, pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } else { + uint32_t ddp_bytes; + + /* Account for PDUs silently received via DDP. */ + ddp_bytes = data_offset - + (cb->data_offset + cb->data_xfered); + cb->data_xfered += ddp_bytes; +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u previous ddp_bytes %u", + __func__, qp->toep->tid, ddp_bytes); +#endif + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_ddp_octets, + ddp_bytes); + } + } + + if ((cb->data_xfered + data_len == cb->data_len) != + ((pdu->hdr->flags & NVME_TCP_C2H_DATA_FLAGS_LAST_PDU) != 0)) { + mtx_unlock(&qp->rx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR, 0, pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + cb->data_xfered += data_len; + original_cid = cb->original_cid; + + if (CHE_TAG_IS_FL(cid)) { + data_offset -= cb->data_offset; + if (cb->data_xfered == cb->data_len) + che_remove_command_buffer(&qp->rx_buffers, cb); + else + che_hold_command_buffer(cb); + mtx_unlock(&qp->rx_buffers.lock); + + if ((pdu->hdr->flags & NVME_TCP_C2H_DATA_FLAGS_SUCCESS) != 0) { + /* + * Free the CID as the command has now been + * completed. + */ + cid = CHE_RAW_FL_TAG(cid); + mtx_lock(&qp->fl_cid_lock); + MPASS(FL_CID_ISACTIVE(cid, qp->fl_cid_set)); + MPASS(original_cid == qp->fl_cids[cid]); + FL_CID_FREE(cid, qp->fl_cid_set); + mtx_unlock(&qp->fl_cid_lock); + } + + mbuf_copyto_io(pdu->m->m_next, 0, data_len, &cb->io, + data_offset); + + che_release_command_buffer(cb); + } else { + if ((pdu->hdr->flags & NVME_TCP_C2H_DATA_FLAGS_SUCCESS) != 0) { + /* + * Free the command buffer and STAG as the + * command has now been completed. + */ + che_free_ddp_tag(qp, cb, cid); + mtx_unlock(&qp->rx_buffers.lock); + che_release_command_buffer(cb); + } else + mtx_unlock(&qp->rx_buffers.lock); + } + + if ((pdu->hdr->flags & NVME_TCP_C2H_DATA_FLAGS_SUCCESS) != 0) { + struct nvme_completion cqe; + struct nvmf_capsule *nc; + + memset(&cqe, 0, sizeof(cqe)); + cqe.cid = original_cid; + + nc = nvmf_allocate_response(&qp->qp, &cqe, M_WAITOK); + nc->nc_sqhd_valid = false; + + nvmf_capsule_received(&qp->qp, nc); + } + + nvmf_che_free_pdu(pdu); + return (0); +} + +/* Called when m_free drops refcount to 0. */ +static void +nvmf_che_mbuf_done(struct mbuf *m) +{ + struct nvmf_che_command_buffer *cb = m->m_ext.ext_arg1; + + che_free_command_buffer(cb); +} + +static struct mbuf * +nvmf_che_mbuf(void *arg, int how, void *data, size_t len) +{ + struct nvmf_che_command_buffer *cb = arg; + struct mbuf *m; + + m = m_get(how, MT_DATA); + m->m_flags |= M_RDONLY; + m_extaddref(m, data, len, &cb->refs, nvmf_che_mbuf_done, cb, NULL); + m->m_len = len; + return (m); +} + +static void +nvmf_che_free_mext_pg(struct mbuf *m) +{ + struct nvmf_che_command_buffer *cb = m->m_ext.ext_arg1; + + M_ASSERTEXTPG(m); + che_release_command_buffer(cb); +} + +static struct mbuf * +nvmf_che_mext_pg(void *arg, int how) +{ + struct nvmf_che_command_buffer *cb = arg; + struct mbuf *m; + + m = mb_alloc_ext_pgs(how, nvmf_che_free_mext_pg, M_RDONLY); + m->m_ext.ext_arg1 = cb; + che_hold_command_buffer(cb); + return (m); +} + +/* + * Return an mbuf chain for a range of data belonging to a command + * buffer. + * + * The mbuf chain uses M_EXT mbufs which hold references on the + * command buffer so that it remains "alive" until the data has been + * fully transmitted. If truncate_ok is true, then the mbuf chain + * might return a short chain to avoid gratuitously splitting up a + * page. + */ +static struct mbuf * +nvmf_che_command_buffer_mbuf(struct nvmf_che_command_buffer *cb, + uint32_t data_offset, uint32_t data_len, uint32_t *actual_len, + bool can_truncate) +{ + struct mbuf *m; + size_t len; + + m = memdesc_alloc_ext_mbufs(&cb->io.io_mem, nvmf_che_mbuf, + nvmf_che_mext_pg, cb, M_WAITOK, data_offset, data_len, &len, + can_truncate); + if (actual_len != NULL) + *actual_len = len; + return (m); +} + +/* NB: cid and ttag and little-endian already. */ +static void +che_send_h2c_pdu(struct nvmf_che_qpair *qp, uint16_t cid, uint16_t ttag, + uint32_t data_offset, struct mbuf *m, size_t len, bool last_pdu) +{ + struct nvme_tcp_h2c_data_hdr h2c; + struct mbuf *top; + + memset(&h2c, 0, sizeof(h2c)); + h2c.common.pdu_type = NVME_TCP_PDU_TYPE_H2C_DATA; + if (last_pdu) + h2c.common.flags |= NVME_TCP_H2C_DATA_FLAGS_LAST_PDU; + h2c.cccid = cid; + h2c.ttag = ttag; + h2c.datao = htole32(data_offset); + h2c.datal = htole32(len); + + top = nvmf_che_construct_pdu(qp, &h2c, sizeof(h2c), m, len); + nvmf_che_write_pdu(qp, top); +} + +static int +nvmf_che_handle_r2t(struct nvmf_che_qpair *qp, struct nvmf_che_rxpdu *pdu) +{ + const struct nvme_tcp_r2t_hdr *r2t; + struct nvmf_che_command_buffer *cb; + uint32_t data_len, data_offset; + + r2t = (const void *)pdu->hdr; + + mtx_lock(&qp->tx_buffers.lock); + cb = che_find_command_buffer(&qp->tx_buffers, r2t->cccid); + if (cb == NULL) { + mtx_unlock(&qp->tx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD, + offsetof(struct nvme_tcp_r2t_hdr, cccid), pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + data_offset = le32toh(r2t->r2to); + if (data_offset != cb->data_xfered) { + mtx_unlock(&qp->tx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR, 0, pdu->m, + pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + /* + * XXX: The spec does not specify how to handle R2T tranfers + * out of range of the original command. + */ + data_len = le32toh(r2t->r2tl); + if (data_offset + data_len > cb->data_len) { + mtx_unlock(&qp->tx_buffers.lock); + nvmf_che_report_error(qp, + NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_OUT_OF_RANGE, 0, + pdu->m, pdu->hdr->hlen); + nvmf_che_free_pdu(pdu); + return (EBADMSG); + } + + cb->data_xfered += data_len; + if (cb->data_xfered == cb->data_len) + che_remove_command_buffer(&qp->tx_buffers, cb); + else + che_hold_command_buffer(cb); + mtx_unlock(&qp->tx_buffers.lock); + + /* + * Queue one or more H2C_DATA PDUs containing the requested + * data. + */ + while (data_len > 0) { + struct mbuf *m; + uint32_t sent, todo; + + todo = min(data_len, qp->max_tx_data); + m = nvmf_che_command_buffer_mbuf(cb, data_offset, todo, &sent, + todo < data_len); + che_send_h2c_pdu(qp, r2t->cccid, r2t->ttag, data_offset, m, + sent, sent == data_len); + + data_offset += sent; + data_len -= sent; + } + + che_release_command_buffer(cb); + nvmf_che_free_pdu(pdu); + return (0); +} + +static int +nvmf_che_dispatch_pdu(struct nvmf_che_qpair *qp, struct nvmf_che_rxpdu *pdu) +{ + /* + * The PDU header should always be contiguous in the mbuf from + * CPL_NVMT_CMP. + */ + pdu->hdr = mtod(pdu->m, void *); + KASSERT(pdu->m->m_len == pdu->hdr->hlen + + ((pdu->hdr->flags & NVME_TCP_CH_FLAGS_HDGSTF) != 0 ? + sizeof(uint32_t) : 0), + ("%s: mismatched PDU header mbuf length", __func__)); + + switch (pdu->hdr->pdu_type) { + default: + __assert_unreachable(); + break; + case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: + case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: + return (nvmf_che_handle_term_req(pdu)); + case NVME_TCP_PDU_TYPE_CAPSULE_CMD: + return (nvmf_che_save_command_capsule(qp, pdu)); + case NVME_TCP_PDU_TYPE_CAPSULE_RESP: + return (nvmf_che_save_response_capsule(qp, pdu)); + case NVME_TCP_PDU_TYPE_H2C_DATA: + return (nvmf_che_handle_h2c_data(qp, pdu)); + case NVME_TCP_PDU_TYPE_C2H_DATA: + return (nvmf_che_handle_c2h_data(qp, pdu)); + case NVME_TCP_PDU_TYPE_R2T: + return (nvmf_che_handle_r2t(qp, pdu)); + } +} + +static int +nvmf_che_attach_pdu_data(struct nvmf_che_qpair *qp, struct nvmf_che_rxpdu *pdu) +{ + struct socket *so = qp->so; + struct mbuf *m, *n; + uint32_t tcp_seq; + size_t len; + int error; + + /* Check for DDP data. */ + if (pdu->ddp) { + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_ddp_pdus, 1); + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_ddp_octets, + pdu->data_len); + return (0); + } + + error = 0; + len = pdu->data_len; + tcp_seq = pdu->m->m_pkthdr.nvmf_tcp_seq; + m = pdu->m; + SOCKBUF_LOCK(&so->so_rcv); + while (len > 0) { + n = mbufq_dequeue(&qp->rx_data); + KASSERT(n != NULL, ("%s: missing %zu data", __func__, len)); + if (n == NULL) { + error = ENOBUFS; + break; + } + + KASSERT(n->m_pkthdr.nvmf_tcp_seq == tcp_seq, + ("%s: TCP seq mismatch", __func__)); + KASSERT(n->m_pkthdr.len <= len, + ("%s: too much data", __func__)); + if (n->m_pkthdr.nvmf_tcp_seq != tcp_seq || + n->m_pkthdr.len > len) { + m_freem(n); + error = ENOBUFS; + break; + } + +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u len %d seq %u", __func__, + qp->toep->tid, n->m_pkthdr.len, n->m_pkthdr.nvmf_tcp_seq); +#endif + pdu->m->m_pkthdr.len += n->m_pkthdr.len; + len -= n->m_pkthdr.len; + tcp_seq += n->m_pkthdr.len; + m_demote_pkthdr(n); + m->m_next = n; + m = m_last(n); + } + SOCKBUF_UNLOCK(&so->so_rcv); + + if (error == 0) { + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_fl_pdus, 1); + counter_u64_add(qp->toep->ofld_rxq->rx_nvme_fl_octets, + pdu->data_len); + } + return (error); +} + +static void +nvmf_che_receive(void *arg) +{ + struct nvmf_che_qpair *qp = arg; + struct socket *so = qp->so; + struct nvmf_che_rxpdu pdu; + struct mbuf *m; + int error, terror; + + SOCKBUF_LOCK(&so->so_rcv); + while (!qp->rx_shutdown) { + /* Wait for a PDU. */ + if (so->so_error != 0 || so->so_rerror != 0) { + if (so->so_error != 0) + error = so->so_error; + else + error = so->so_rerror; + SOCKBUF_UNLOCK(&so->so_rcv); + error: + nvmf_qpair_error(&qp->qp, error); + SOCKBUF_LOCK(&so->so_rcv); + while (!qp->rx_shutdown) + cv_wait(&qp->rx_cv, SOCKBUF_MTX(&so->so_rcv)); + break; + } + + m = mbufq_dequeue(&qp->rx_pdus); + if (m == NULL) { + if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0) { + error = 0; + SOCKBUF_UNLOCK(&so->so_rcv); + goto error; + } + cv_wait(&qp->rx_cv, SOCKBUF_MTX(&so->so_rcv)); + continue; + } + SOCKBUF_UNLOCK(&so->so_rcv); + + pdu.m = m; + pdu.hdr = mtod(m, const void *); + pdu.ddp = (m->m_pkthdr.nvmf_cpl_status & CMP_STATUS_DDP) != 0; + + error = nvmf_che_validate_pdu(qp, &pdu); + if (error == 0 && pdu.data_len != 0) + error = nvmf_che_attach_pdu_data(qp, &pdu); + if (error != 0) + nvmf_che_free_pdu(&pdu); + else + error = nvmf_che_dispatch_pdu(qp, &pdu); + if (error != 0) { + /* + * If we received a termination request, close + * the connection immediately. + */ + if (error == ECONNRESET) + goto error; + + /* + * Wait for up to 30 seconds for the socket to + * be closed by the other end. + */ + SOCKBUF_LOCK(&so->so_rcv); + if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0) { + terror = cv_timedwait(&qp->rx_cv, + SOCKBUF_MTX(&so->so_rcv), 30 * hz); + if (terror == ETIMEDOUT) + printf("NVMe/TCP: Timed out after sending terminate request\n"); + } + SOCKBUF_UNLOCK(&so->so_rcv); + goto error; + } + + SOCKBUF_LOCK(&so->so_rcv); + } + SOCKBUF_UNLOCK(&so->so_rcv); + kthread_exit(); +} + +static int +nvmf_che_soupcall_receive(struct socket *so, void *arg, int waitflag) +{ + struct nvmf_che_qpair *qp = arg; + + cv_signal(&qp->rx_cv); + return (SU_OK); +} + +static int +do_nvmt_data(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) +{ + struct adapter *sc = iq->adapter; + struct nvmf_che_adapter *nca = sc->nvme_ulp_softc; + const struct cpl_nvmt_data *cpl; + u_int tid; + struct toepcb *toep; + struct nvmf_che_qpair *qp; + struct socket *so; + struct inpcb *inp; + struct tcpcb *tp; + int len __diagused; + + if (nca->nvmt_data_iqe) { + cpl = (const void *)(rss + 1); + } else { + cpl = mtod(m, const void *); + + /* strip off CPL header */ + m_adj(m, sizeof(*cpl)); + } + tid = GET_TID(cpl); + toep = lookup_tid(sc, tid); + + KASSERT(toep->tid == tid, ("%s: toep tid/atid mismatch", __func__)); + + len = m->m_pkthdr.len; + + KASSERT(len == be16toh(cpl->length), + ("%s: payload length mismatch", __func__)); + + inp = toep->inp; + INP_WLOCK(inp); + if (inp->inp_flags & INP_DROPPED) { + CTR(KTR_CXGBE, "%s: tid %u, rx (%d bytes), inp_flags 0x%x", + __func__, tid, len, inp->inp_flags); + INP_WUNLOCK(inp); + m_freem(m); + return (0); + } + + /* Save TCP sequence number. */ + m->m_pkthdr.nvmf_tcp_seq = be32toh(cpl->seq); + + qp = toep->ulpcb; + so = qp->so; + SOCKBUF_LOCK(&so->so_rcv); + mbufq_enqueue(&qp->rx_data, m); + SOCKBUF_UNLOCK(&so->so_rcv); + + tp = intotcpcb(inp); + tp->t_rcvtime = ticks; + +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u len %d seq %u", __func__, tid, len, + be32toh(cpl->seq)); +#endif + + INP_WUNLOCK(inp); + return (0); +} + +static int +do_nvmt_cmp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) +{ + struct adapter *sc = iq->adapter; + const struct cpl_nvmt_cmp *cpl = mtod(m, const void *); + u_int tid = GET_TID(cpl); + struct toepcb *toep = lookup_tid(sc, tid); + struct nvmf_che_qpair *qp = toep->ulpcb; + struct socket *so = qp->so; + struct inpcb *inp = toep->inp; + u_int hlen __diagused; + bool empty; + + KASSERT(toep->tid == tid, ("%s: toep tid/atid mismatch", __func__)); + KASSERT(!(toep->flags & TPF_SYNQE), + ("%s: toep %p claims to be a synq entry", __func__, toep)); + + /* strip off CPL header */ + m_adj(m, sizeof(*cpl)); + hlen = m->m_pkthdr.len; + + KASSERT(hlen == be16toh(cpl->length), + ("%s: payload length mismatch", __func__)); + + INP_WLOCK(inp); + if (inp->inp_flags & INP_DROPPED) { + CTR(KTR_CXGBE, "%s: tid %u, rx (hlen %u), inp_flags 0x%x", + __func__, tid, hlen, inp->inp_flags); + INP_WUNLOCK(inp); + m_freem(m); + return (0); + } + +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u hlen %u seq %u status %u", __func__, tid, + hlen, be32toh(cpl->seq), cpl->status); +#endif + + /* Save TCP sequence number and CPL status. */ + m->m_pkthdr.nvmf_tcp_seq = be32toh(cpl->seq); + m->m_pkthdr.nvmf_cpl_status = cpl->status; + + SOCKBUF_LOCK(&so->so_rcv); + empty = mbufq_len(&qp->rx_pdus) == 0; + mbufq_enqueue(&qp->rx_pdus, m); + SOCKBUF_UNLOCK(&so->so_rcv); + INP_WUNLOCK(inp); + if (empty) + cv_signal(&qp->rx_cv); + return (0); +} + +static uint16_t +che_alloc_fl_cid(struct nvmf_che_qpair *qp, uint16_t original_cid) +{ + uint16_t new_cid; + + mtx_lock(&qp->fl_cid_lock); + new_cid = FL_CID_FINDFREE_AT(qp->fl_cid_set, qp->next_cid); + if (new_cid == 0) { + new_cid = FL_CID_FINDFREE_AT(qp->fl_cid_set, 0); + MPASS(new_cid != 0); + } + new_cid--; + FL_CID_BUSY(new_cid, qp->fl_cid_set); + if (new_cid == CHE_MAX_FL_TAG) + qp->next_cid = 0; + else + qp->next_cid = new_cid + 1; + qp->fl_cids[new_cid] = original_cid; + mtx_unlock(&qp->fl_cid_lock); + + return (new_cid | CHE_FL_TAG_MASK); +} + +static uint16_t +che_alloc_ddp_cid(struct nvmf_che_qpair *qp, struct nvmf_che_command_buffer *cb) +{ + mtx_assert(&qp->rx_buffers.lock, MA_OWNED); + + return (che_alloc_ddp_tag(qp, cb)); +} + +static struct mbuf * +che_command_pdu(struct nvmf_che_qpair *qp, struct nvmf_che_capsule *cc) +{ + struct nvmf_capsule *nc = &cc->nc; + struct nvmf_che_command_buffer *cb; + struct nvme_sgl_descriptor *sgl; + struct nvme_tcp_cmd cmd; + struct mbuf *top, *m; + uint16_t cid; + bool use_icd; + + use_icd = false; + cb = NULL; + m = NULL; + + if (nc->nc_data.io_len != 0) { + cb = che_alloc_command_buffer(qp, &nc->nc_data, 0, + nc->nc_data.io_len, nc->nc_sqe.cid); + cb->original_cid = nc->nc_sqe.cid; + + if (nc->nc_send_data && nc->nc_data.io_len <= qp->max_icd) { + cid = che_alloc_fl_cid(qp, nc->nc_sqe.cid); + use_icd = true; + m = nvmf_che_command_buffer_mbuf(cb, 0, + nc->nc_data.io_len, NULL, false); + cb->data_xfered = nc->nc_data.io_len; + che_release_command_buffer(cb); + } else if (nc->nc_send_data) { + cid = che_alloc_fl_cid(qp, nc->nc_sqe.cid); + cb->cid = htole16(cid); + mtx_lock(&qp->tx_buffers.lock); + che_add_command_buffer(&qp->tx_buffers, cb); + mtx_unlock(&qp->tx_buffers.lock); + } else { + mtx_lock(&qp->rx_buffers.lock); + cid = che_alloc_ddp_cid(qp, cb); + if (cid == CHE_DDP_NO_TAG) { + cid = che_alloc_fl_cid(qp, nc->nc_sqe.cid); + che_add_command_buffer(&qp->rx_buffers, cb); + } + cb->cid = htole16(cid); + mtx_unlock(&qp->rx_buffers.lock); + } + } else + cid = che_alloc_fl_cid(qp, nc->nc_sqe.cid); + +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, "%s: tid %u allocated cid 0x%04x for 0x%04x", __func__, + qp->toep->tid, cid, nc->nc_sqe.cid); +#endif + memset(&cmd, 0, sizeof(cmd)); + cmd.common.pdu_type = NVME_TCP_PDU_TYPE_CAPSULE_CMD; + cmd.ccsqe = nc->nc_sqe; + cmd.ccsqe.cid = htole16(cid); + + /* Populate SGL in SQE. */ + sgl = &cmd.ccsqe.sgl; + memset(sgl, 0, sizeof(*sgl)); + sgl->address = 0; + sgl->length = htole32(nc->nc_data.io_len); + if (use_icd) { + /* Use in-capsule data. */ + sgl->type = NVME_SGL_TYPE_ICD; + } else { + /* Use a command buffer. */ + sgl->type = NVME_SGL_TYPE_COMMAND_BUFFER; + } + + top = nvmf_che_construct_pdu(qp, &cmd, sizeof(cmd), m, m != NULL ? + nc->nc_data.io_len : 0); + return (top); +} + +static struct mbuf * +che_response_pdu(struct nvmf_che_qpair *qp, struct nvmf_che_capsule *cc) +{ + struct nvmf_capsule *nc = &cc->nc; + struct nvme_tcp_rsp rsp; + + memset(&rsp, 0, sizeof(rsp)); + rsp.common.pdu_type = NVME_TCP_PDU_TYPE_CAPSULE_RESP; + rsp.rccqe = nc->nc_cqe; + + return (nvmf_che_construct_pdu(qp, &rsp, sizeof(rsp), NULL, 0)); +} + +static struct mbuf * +capsule_to_pdu(struct nvmf_che_qpair *qp, struct nvmf_che_capsule *cc) +{ + if (cc->nc.nc_qe_len == sizeof(struct nvme_command)) + return (che_command_pdu(qp, cc)); + else + return (che_response_pdu(qp, cc)); +} + +static void +nvmf_che_send(void *arg) +{ + struct nvmf_che_qpair *qp = arg; + struct nvmf_che_capsule *cc; + struct socket *so = qp->so; + struct mbuf *m; + int error; + + m = NULL; + SOCKBUF_LOCK(&so->so_snd); + while (!qp->tx_shutdown) { + if (so->so_error != 0) { + error = so->so_error; + SOCKBUF_UNLOCK(&so->so_snd); + m_freem(m); + nvmf_qpair_error(&qp->qp, error); + SOCKBUF_LOCK(&so->so_snd); + while (!qp->tx_shutdown) + cv_wait(&qp->tx_cv, SOCKBUF_MTX(&so->so_snd)); + break; + } + + if (STAILQ_EMPTY(&qp->tx_capsules)) { + cv_wait(&qp->tx_cv, SOCKBUF_MTX(&so->so_snd)); + continue; + } + + /* Convert a capsule into a PDU. */ + cc = STAILQ_FIRST(&qp->tx_capsules); + STAILQ_REMOVE_HEAD(&qp->tx_capsules, link); + SOCKBUF_UNLOCK(&so->so_snd); + + m = capsule_to_pdu(qp, cc); + che_release_capsule(cc); + + nvmf_che_write_pdu(qp, m); + + SOCKBUF_LOCK(&so->so_snd); + } + SOCKBUF_UNLOCK(&so->so_snd); + kthread_exit(); +} + +static int +nvmf_che_setsockopt(struct socket *so, u_int sspace, u_int rspace) +{ + struct sockopt opt; + int error, one = 1; + + /* Don't lower the buffer sizes, just enforce a minimum. */ + SOCKBUF_LOCK(&so->so_snd); + if (sspace < so->so_snd.sb_hiwat) + sspace = so->so_snd.sb_hiwat; + SOCKBUF_UNLOCK(&so->so_snd); + SOCKBUF_LOCK(&so->so_rcv); + if (rspace < so->so_rcv.sb_hiwat) + rspace = so->so_rcv.sb_hiwat; + SOCKBUF_UNLOCK(&so->so_rcv); + + error = soreserve(so, sspace, rspace); + if (error != 0) + return (error); + SOCKBUF_LOCK(&so->so_snd); + so->so_snd.sb_flags |= SB_AUTOSIZE; + SOCKBUF_UNLOCK(&so->so_snd); + SOCKBUF_LOCK(&so->so_rcv); + so->so_rcv.sb_flags |= SB_AUTOSIZE; + SOCKBUF_UNLOCK(&so->so_rcv); + + /* + * Disable Nagle. + */ + bzero(&opt, sizeof(opt)); + opt.sopt_dir = SOPT_SET; + opt.sopt_level = IPPROTO_TCP; + opt.sopt_name = TCP_NODELAY; + opt.sopt_val = &one; + opt.sopt_valsize = sizeof(one); + error = sosetopt(so, &opt); + if (error != 0) + return (error); + + return (0); +} + +static void +t4_nvme_set_tcb_field(struct toepcb *toep, uint16_t word, uint64_t mask, + uint64_t val) +{ + struct adapter *sc = td_adapter(toep->td); + + t4_set_tcb_field(sc, &toep->ofld_txq->wrq, toep, word, mask, val, 0, 0); +} + +static void +set_ulp_mode_nvme(struct toepcb *toep, u_int ulp_submode, uint8_t rxpda) +{ + uint64_t val; + + CTR(KTR_CXGBE, "%s: tid %u, ULP_MODE_NVMET, submode=%#x, rxpda=%u", + __func__, toep->tid, ulp_submode, rxpda); + + val = V_TCB_ULP_TYPE(ULP_MODE_NVMET) | V_TCB_ULP_RAW(ulp_submode); + t4_nvme_set_tcb_field(toep, W_TCB_ULP_TYPE, + V_TCB_ULP_TYPE(M_TCB_ULP_TYPE) | V_TCB_ULP_RAW(M_TCB_ULP_RAW), val); + + val = V_TF_RX_FLOW_CONTROL_DISABLE(1ULL); + t4_nvme_set_tcb_field(toep, W_TCB_T_FLAGS, val, val); + + val = V_TCB_RSVD((rxpda / 4) - 1); + t4_nvme_set_tcb_field(toep, W_TCB_RSVD, V_TCB_RSVD(M_TCB_RSVD), val); + + /* 0 disables CPL_NVMT_CMP_IMM which is not useful in this driver. */ + val = 0; + t4_nvme_set_tcb_field(toep, W_TCB_CMP_IMM_SZ, + V_TCB_CMP_IMM_SZ(M_TCB_CMP_IMM_SZ), val); +} + +static u_int +pdu_max_data_len(const nvlist_t *nvl, u_int max_pdu_len, u_int hlen, + uint8_t pda) +{ + u_int max_data_len; + + if (nvlist_get_bool(nvl, "header_digests")) + hlen += sizeof(uint32_t); + hlen = roundup(hlen, pda); + max_data_len = max_pdu_len - hlen; + if (nvlist_get_bool(nvl, "data_digests")) + max_data_len -= sizeof(uint32_t); + return (max_data_len); +} + +static struct nvmf_qpair * +che_allocate_qpair(bool controller, const nvlist_t *nvl) +{ + struct nvmf_che_adapter *nca; + struct nvmf_che_qpair *qp; + struct adapter *sc; + struct file *fp; + struct socket *so; + struct inpcb *inp; + struct tcpcb *tp; + struct toepcb *toep; + cap_rights_t rights; + u_int max_tx_pdu_len, num_ddp_tags; + int error, ulp_submode; + + if (!nvlist_exists_number(nvl, "fd") || + !nvlist_exists_number(nvl, "rxpda") || + !nvlist_exists_number(nvl, "txpda") || + !nvlist_exists_bool(nvl, "header_digests") || + !nvlist_exists_bool(nvl, "data_digests") || + !nvlist_exists_number(nvl, "maxr2t") || + !nvlist_exists_number(nvl, "maxh2cdata") || + !nvlist_exists_number(nvl, "max_icd")) + return (NULL); + + error = fget(curthread, nvlist_get_number(nvl, "fd"), + cap_rights_init_one(&rights, CAP_SOCK_CLIENT), &fp); + if (error != 0) + return (NULL); + if (fp->f_type != DTYPE_SOCKET) { + fdrop(fp, curthread); + return (NULL); + } + so = fp->f_data; + if (so->so_type != SOCK_STREAM || + so->so_proto->pr_protocol != IPPROTO_TCP) { + fdrop(fp, curthread); + return (NULL); + } + + sc = find_offload_adapter(so); + if (sc == NULL) { + fdrop(fp, curthread); + return (NULL); + } + nca = sc->nvme_ulp_softc; + + /* + * Controller: Require advertised MAXH2CDATA to be small + * enough. + */ + if (controller) { + u_int max_rx_data; + + max_rx_data = pdu_max_data_len(nvl, nca->max_receive_pdu, + sizeof(struct nvme_tcp_h2c_data_hdr), + nvlist_get_number(nvl, "rxpda")); + if (nvlist_get_number(nvl, "maxh2cdata") > max_rx_data) { + fdrop(fp, curthread); + return (NULL); + } + } + + /* + * Host: Require the queue size to be small enough that all of + * the command ids allocated by nvmf(4) will fit in the + * unallocated range. + * + * XXX: Alternatively this driver could just queue commands + * when an unallocated ID isn't available. + */ + if (!controller) { + u_int num_commands; + + num_commands = nvlist_get_number(nvl, "qsize") - 1; + if (nvlist_get_bool(nvl, "admin")) + num_commands += 8; /* Max AER */ + if (num_commands > CHE_NUM_FL_TAGS) { + fdrop(fp, curthread); + return (NULL); + } + } + + qp = malloc(sizeof(*qp), M_NVMF_CHE, M_WAITOK | M_ZERO); + qp->txpda = nvlist_get_number(nvl, "txpda"); + qp->rxpda = nvlist_get_number(nvl, "rxpda"); + qp->header_digests = nvlist_get_bool(nvl, "header_digests"); + qp->data_digests = nvlist_get_bool(nvl, "data_digests"); + qp->maxr2t = nvlist_get_number(nvl, "maxr2t"); + if (controller) + qp->maxh2cdata = nvlist_get_number(nvl, "maxh2cdata"); + + if (controller) { + /* NB: maxr2t is 0's based. */ + qp->num_fl_ttags = MIN(CHE_NUM_FL_TAGS, + nvlist_get_number(nvl, "qsize") * + ((uint64_t)qp->maxr2t + 1)); + qp->open_fl_ttags = mallocarray(qp->num_fl_ttags, + sizeof(*qp->open_fl_ttags), M_NVMF_CHE, M_WAITOK | M_ZERO); + } else { + qp->fl_cids = mallocarray(CHE_NUM_FL_TAGS, + sizeof(*qp->fl_cids), M_NVMF_CHE, M_WAITOK | M_ZERO); + qp->fl_cid_set = malloc(sizeof(*qp->fl_cid_set), M_NVMF_CHE, + M_WAITOK); + FL_CID_INIT(qp->fl_cid_set); + mtx_init(&qp->fl_cid_lock, "nvmf/che fl cids", NULL, MTX_DEF); + } + + inp = sotoinpcb(so); + INP_WLOCK(inp); + tp = intotcpcb(inp); + if (inp->inp_flags & INP_DROPPED) { + INP_WUNLOCK(inp); + free(qp->fl_cid_set, M_NVMF_CHE); + free(qp->fl_cids, M_NVMF_CHE); + free(qp->open_fl_ttags, M_NVMF_CHE); + free(qp, M_NVMF_CHE); + fdrop(fp, curthread); + return (NULL); + } + + MPASS(tp->t_flags & TF_TOE); + MPASS(tp->tod != NULL); + MPASS(tp->t_toe != NULL); + toep = tp->t_toe; + MPASS(toep->vi->adapter == sc); + + if (ulp_mode(toep) != ULP_MODE_NONE) { + INP_WUNLOCK(inp); + free(qp->fl_cid_set, M_NVMF_CHE); + free(qp->fl_cids, M_NVMF_CHE); + free(qp->open_fl_ttags, M_NVMF_CHE); + free(qp, M_NVMF_CHE); + fdrop(fp, curthread); + return (NULL); + } + + /* Claim socket from file descriptor. */ + fp->f_ops = &badfileops; + fp->f_data = NULL; + + qp->so = so; + qp->toep = toep; + qp->nca = nca; + refcount_init(&qp->refs, 1); + + /* NB: C2H and H2C headers are the same size. */ + qp->max_rx_data = pdu_max_data_len(nvl, nca->max_receive_pdu, + sizeof(struct nvme_tcp_c2h_data_hdr), qp->rxpda); + qp->max_tx_data = pdu_max_data_len(nvl, nca->max_transmit_pdu, + sizeof(struct nvme_tcp_c2h_data_hdr), qp->txpda); + if (!controller) { + qp->max_tx_data = min(qp->max_tx_data, + nvlist_get_number(nvl, "maxh2cdata")); + qp->max_icd = min(nvlist_get_number(nvl, "max_icd"), + pdu_max_data_len(nvl, nca->max_transmit_pdu, + sizeof(struct nvme_tcp_cmd), qp->txpda)); + } else { + /* + * IOCCSZ represents the size of a logical command + * capsule including the 64 byte SQE and the + * in-capsule data. Use pdu_max_data_len to compute + * the maximum supported ICD length. + */ + qp->max_ioccsz = rounddown(pdu_max_data_len(nvl, + nca->max_receive_pdu, sizeof(struct nvme_tcp_cmd), + qp->rxpda), 16) + sizeof(struct nvme_command); + } + + ulp_submode = 0; + if (qp->header_digests) + ulp_submode |= FW_NVMET_ULPSUBMODE_HCRC; + if (qp->data_digests) + ulp_submode |= FW_NVMET_ULPSUBMODE_DCRC; + if (!controller) + ulp_submode |= FW_NVMET_ULPSUBMODE_ING_DIR; + + max_tx_pdu_len = sizeof(struct nvme_tcp_h2c_data_hdr); + if (qp->header_digests) + max_tx_pdu_len += sizeof(uint32_t); + max_tx_pdu_len = roundup(max_tx_pdu_len, qp->txpda); + max_tx_pdu_len += qp->max_tx_data; + if (qp->data_digests) + max_tx_pdu_len += sizeof(uint32_t); + + /* TODO: ISO limits */ + + if (controller) { + /* Use the SUCCESS flag if SQ flow control is disabled. */ + qp->send_success = !nvlist_get_bool(nvl, "sq_flow_control"); + } + + toep->params.ulp_mode = ULP_MODE_NVMET; + toep->ulpcb = qp; + + send_txdataplen_max_flowc_wr(sc, toep, + roundup(/* max_iso_pdus * */ max_tx_pdu_len, tp->t_maxseg)); + set_ulp_mode_nvme(toep, ulp_submode, qp->rxpda); + INP_WUNLOCK(inp); + + fdrop(fp, curthread); + + error = nvmf_che_setsockopt(so, max_tx_pdu_len, nca->max_receive_pdu); + if (error != 0) { + free(qp->fl_cid_set, M_NVMF_CHE); + free(qp->fl_cids, M_NVMF_CHE); + free(qp->open_fl_ttags, M_NVMF_CHE); + free(qp, M_NVMF_CHE); + return (NULL); + } + + num_ddp_tags = ddp_tags_per_qp; + if (num_ddp_tags > 0) { + qp->tpt_offset = t4_stag_alloc(sc, num_ddp_tags); + if (qp->tpt_offset != T4_STAG_UNSET) { +#ifdef VERBOSE_TRACES + CTR(KTR_CXGBE, + "%s: tid %u using %u tags at offset 0x%x", + __func__, toep->tid, num_ddp_tags, qp->tpt_offset); +#endif + qp->num_ddp_tags = num_ddp_tags; + qp->open_ddp_tags = mallocarray(qp->num_ddp_tags, + sizeof(*qp->open_ddp_tags), M_NVMF_CHE, M_WAITOK | + M_ZERO); + + t4_nvme_set_tcb_field(toep, W_TCB_TPT_OFFSET, + M_TCB_TPT_OFFSET, V_TCB_TPT_OFFSET(qp->tpt_offset)); + } + } + + TAILQ_INIT(&qp->rx_buffers.head); + TAILQ_INIT(&qp->tx_buffers.head); + mtx_init(&qp->rx_buffers.lock, "nvmf/che rx buffers", NULL, MTX_DEF); + mtx_init(&qp->tx_buffers.lock, "nvmf/che tx buffers", NULL, MTX_DEF); + + cv_init(&qp->rx_cv, "-"); + cv_init(&qp->tx_cv, "-"); + mbufq_init(&qp->rx_data, 0); + mbufq_init(&qp->rx_pdus, 0); + STAILQ_INIT(&qp->tx_capsules); + + /* Register socket upcall for receive to handle remote FIN. */ + SOCKBUF_LOCK(&so->so_rcv); + soupcall_set(so, SO_RCV, nvmf_che_soupcall_receive, qp); + SOCKBUF_UNLOCK(&so->so_rcv); + + /* Spin up kthreads. */ + error = kthread_add(nvmf_che_receive, qp, NULL, &qp->rx_thread, 0, 0, + "nvmef che rx"); + if (error != 0) { + che_free_qpair(&qp->qp); + return (NULL); + } + error = kthread_add(nvmf_che_send, qp, NULL, &qp->tx_thread, 0, 0, + "nvmef che tx"); + if (error != 0) { + che_free_qpair(&qp->qp); + return (NULL); + } + + return (&qp->qp); +} + +static void +che_release_qpair(struct nvmf_che_qpair *qp) +{ + if (refcount_release(&qp->refs)) + free(qp, M_NVMF_CHE); +} + +static void +che_free_qpair(struct nvmf_qpair *nq) +{ + struct nvmf_che_qpair *qp = CQP(nq); + struct nvmf_che_command_buffer *ncb, *cb; + struct nvmf_che_capsule *ncc, *cc; + struct socket *so = qp->so; + struct toepcb *toep = qp->toep; + struct inpcb *inp = sotoinpcb(so); + + /* Shut down kthreads. */ + SOCKBUF_LOCK(&so->so_snd); + qp->tx_shutdown = true; + if (qp->tx_thread != NULL) { + cv_signal(&qp->tx_cv); + mtx_sleep(qp->tx_thread, SOCKBUF_MTX(&so->so_snd), 0, + "nvchetx", 0); + } + SOCKBUF_UNLOCK(&so->so_snd); + + SOCKBUF_LOCK(&so->so_rcv); + qp->rx_shutdown = true; + if (qp->rx_thread != NULL) { + cv_signal(&qp->rx_cv); + mtx_sleep(qp->rx_thread, SOCKBUF_MTX(&so->so_rcv), 0, + "nvcherx", 0); + } + soupcall_clear(so, SO_RCV); + SOCKBUF_UNLOCK(&so->so_rcv); + mbufq_drain(&qp->rx_data); + mbufq_drain(&qp->rx_pdus); + + STAILQ_FOREACH_SAFE(cc, &qp->tx_capsules, link, ncc) { + nvmf_abort_capsule_data(&cc->nc, ECONNABORTED); + che_release_capsule(cc); + } + + cv_destroy(&qp->tx_cv); + cv_destroy(&qp->rx_cv); + + if (qp->open_fl_ttags != NULL) { + for (u_int i = 0; i < qp->num_fl_ttags; i++) { + cb = qp->open_fl_ttags[i]; + if (cb != NULL) { + cb->cc->active_r2ts--; + cb->error = ECONNABORTED; + che_release_command_buffer(cb); + } + } + free(qp->open_fl_ttags, M_NVMF_CHE); + } + if (qp->num_ddp_tags != 0) { + for (u_int i = 0; i < qp->num_ddp_tags; i++) { + cb = qp->open_ddp_tags[i]; + if (cb != NULL) { + if (cb->cc != NULL) + cb->cc->active_r2ts--; + cb->error = ECONNABORTED; + mtx_lock(&qp->rx_buffers.lock); + che_free_ddp_tag(qp, cb, cb->ttag); + mtx_unlock(&qp->rx_buffers.lock); + che_release_command_buffer(cb); + } + } + free(qp->open_ddp_tags, M_NVMF_CHE); + } + + mtx_lock(&qp->rx_buffers.lock); + TAILQ_FOREACH_SAFE(cb, &qp->rx_buffers.head, link, ncb) { + che_remove_command_buffer(&qp->rx_buffers, cb); + mtx_unlock(&qp->rx_buffers.lock); +#ifdef INVARIANTS + if (cb->cc != NULL) + cb->cc->pending_r2ts--; +#endif + cb->error = ECONNABORTED; + che_release_command_buffer(cb); + mtx_lock(&qp->rx_buffers.lock); + } + mtx_destroy(&qp->rx_buffers.lock); + + mtx_lock(&qp->tx_buffers.lock); + TAILQ_FOREACH_SAFE(cb, &qp->tx_buffers.head, link, ncb) { + che_remove_command_buffer(&qp->tx_buffers, cb); + mtx_unlock(&qp->tx_buffers.lock); + cb->error = ECONNABORTED; + che_release_command_buffer(cb); + mtx_lock(&qp->tx_buffers.lock); + } + mtx_destroy(&qp->tx_buffers.lock); + + if (qp->num_ddp_tags != 0) + t4_stag_free(qp->nca->sc, qp->tpt_offset, qp->num_ddp_tags); + + if (!qp->qp.nq_controller) { + free(qp->fl_cids, M_NVMF_CHE); + free(qp->fl_cid_set, M_NVMF_CHE); + mtx_destroy(&qp->fl_cid_lock); + } + + INP_WLOCK(inp); + toep->ulpcb = NULL; + mbufq_drain(&toep->ulp_pduq); + + /* + * Grab a reference to use when waiting for the final CPL to + * be received. If toep->inp is NULL, then + * final_cpl_received() has already been called (e.g. due to + * the peer sending a RST). + */ + if (toep->inp != NULL) { + toep = hold_toepcb(toep); + toep->flags |= TPF_WAITING_FOR_FINAL; + } else + toep = NULL; + INP_WUNLOCK(inp); + + soclose(so); + + /* + * Wait for the socket to fully close. This ensures any + * pending received data has been received (and in particular, + * any data that would be received by DDP has been handled). + */ + if (toep != NULL) { + struct mtx *lock = mtx_pool_find(mtxpool_sleep, toep); + + mtx_lock(lock); + while ((toep->flags & TPF_WAITING_FOR_FINAL) != 0) + mtx_sleep(toep, lock, PSOCK, "conclo2", 0); + mtx_unlock(lock); + free_toepcb(toep); + } + + che_release_qpair(qp); +} + +static uint32_t +che_max_ioccsz(struct nvmf_qpair *nq) +{ + struct nvmf_che_qpair *qp = CQP(nq); + + /* + * Limit the command capsule size so that with maximum ICD it + * fits within the limit of the largest PDU the adapter can + * receive. + */ + return (qp->max_ioccsz); +} + +static uint64_t +che_max_xfer_size(struct nvmf_qpair *nq) +{ + struct nvmf_che_qpair *qp = CQP(nq); + + /* + * Limit host transfers to the size of the data payload in the + * largest PDU the adapter can receive. + */ + return (qp->max_rx_data); +} + +static struct nvmf_capsule * +che_allocate_capsule(struct nvmf_qpair *nq, int how) +{ + struct nvmf_che_qpair *qp = CQP(nq); + struct nvmf_che_capsule *cc; + + cc = malloc(sizeof(*cc), M_NVMF_CHE, how | M_ZERO); + if (cc == NULL) + return (NULL); + refcount_init(&cc->refs, 1); + refcount_acquire(&qp->refs); + return (&cc->nc); +} + +static void +che_release_capsule(struct nvmf_che_capsule *cc) +{ + struct nvmf_che_qpair *qp = CQP(cc->nc.nc_qpair); + + if (!refcount_release(&cc->refs)) + return; + + MPASS(cc->active_r2ts == 0); + MPASS(cc->pending_r2ts == 0); + + nvmf_che_free_pdu(&cc->rx_pdu); + free(cc, M_NVMF_CHE); + che_release_qpair(qp); +} + +static void +che_free_capsule(struct nvmf_capsule *nc) +{ + che_release_capsule(CCAP(nc)); +} + +static int +che_transmit_capsule(struct nvmf_capsule *nc) +{ + struct nvmf_che_qpair *qp = CQP(nc->nc_qpair); + struct nvmf_che_capsule *cc = CCAP(nc); + struct socket *so = qp->so; + + refcount_acquire(&cc->refs); + SOCKBUF_LOCK(&so->so_snd); + STAILQ_INSERT_TAIL(&qp->tx_capsules, cc, link); + cv_signal(&qp->tx_cv); + SOCKBUF_UNLOCK(&so->so_snd); + return (0); +} + +static uint8_t +che_validate_command_capsule(struct nvmf_capsule *nc) +{ + struct nvmf_che_capsule *cc = CCAP(nc); + struct nvme_sgl_descriptor *sgl; + + KASSERT(cc->rx_pdu.hdr != NULL, ("capsule wasn't received")); + + sgl = &nc->nc_sqe.sgl; + switch (sgl->type) { + case NVME_SGL_TYPE_ICD: + if (cc->rx_pdu.data_len != le32toh(sgl->length)) { + printf("NVMe/TCP: Command Capsule with mismatched ICD length\n"); + return (NVME_SC_DATA_SGL_LENGTH_INVALID); + } + break; + case NVME_SGL_TYPE_COMMAND_BUFFER: + if (cc->rx_pdu.data_len != 0) { + printf("NVMe/TCP: Command Buffer SGL with ICD\n"); + return (NVME_SC_INVALID_FIELD); + } + break; + default: + printf("NVMe/TCP: Invalid SGL type in Command Capsule\n"); + return (NVME_SC_SGL_DESCRIPTOR_TYPE_INVALID); + } + + if (sgl->address != 0) { + printf("NVMe/TCP: Invalid SGL offset in Command Capsule\n"); + return (NVME_SC_SGL_OFFSET_INVALID); + } + + return (NVME_SC_SUCCESS); +} + +static size_t +che_capsule_data_len(const struct nvmf_capsule *nc) +{ + MPASS(nc->nc_qe_len == sizeof(struct nvme_command)); + return (le32toh(nc->nc_sqe.sgl.length)); +} + +static void +che_receive_r2t_data(struct nvmf_capsule *nc, uint32_t data_offset, + struct nvmf_io_request *io) +{ + struct nvmf_che_qpair *qp = CQP(nc->nc_qpair); + struct nvmf_che_capsule *cc = CCAP(nc); + struct nvmf_che_command_buffer *cb; + + cb = che_alloc_command_buffer(qp, io, data_offset, io->io_len, + nc->nc_sqe.cid); + + cb->cc = cc; + refcount_acquire(&cc->refs); + + /* + * If this command has too many active R2Ts or there are no + * available transfer tags, queue the request for later. + * + * NB: maxr2t is 0's based. + */ + mtx_lock(&qp->rx_buffers.lock); + if (cc->active_r2ts > qp->maxr2t || + !nvmf_che_allocate_ttag(qp, cb)) { +#ifdef INVARIANTS + cc->pending_r2ts++; +#endif + TAILQ_INSERT_TAIL(&qp->rx_buffers.head, cb, link); + mtx_unlock(&qp->rx_buffers.lock); + return; + } + mtx_unlock(&qp->rx_buffers.lock); + + che_send_r2t(qp, nc->nc_sqe.cid, cb->ttag, data_offset, io->io_len); +} + +static void +che_receive_icd_data(struct nvmf_capsule *nc, uint32_t data_offset, + struct nvmf_io_request *io) +{ + struct nvmf_che_capsule *cc = CCAP(nc); + + /* + * The header is in rx_pdu.m, the padding is discarded, and + * the data starts at rx_pdu.m->m_next. + */ + mbuf_copyto_io(cc->rx_pdu.m->m_next, data_offset, io->io_len, io, 0); + nvmf_complete_io_request(io, io->io_len, 0); +} + +static int +che_receive_controller_data(struct nvmf_capsule *nc, uint32_t data_offset, + struct nvmf_io_request *io) +{ + struct nvme_sgl_descriptor *sgl; + size_t data_len; + + if (nc->nc_qe_len != sizeof(struct nvme_command) || + !nc->nc_qpair->nq_controller) + return (EINVAL); + + sgl = &nc->nc_sqe.sgl; + data_len = le32toh(sgl->length); + if (data_offset + io->io_len > data_len) + return (EFBIG); + + if (sgl->type == NVME_SGL_TYPE_ICD) + che_receive_icd_data(nc, data_offset, io); + else + che_receive_r2t_data(nc, data_offset, io); + return (0); +} + +/* NB: cid is little-endian already. */ +static void +che_send_c2h_pdu(struct nvmf_che_qpair *qp, uint16_t cid, uint32_t data_offset, + struct mbuf *m, size_t len, bool last_pdu, bool success) +{ + struct nvme_tcp_c2h_data_hdr c2h; + struct mbuf *top; + + memset(&c2h, 0, sizeof(c2h)); + c2h.common.pdu_type = NVME_TCP_PDU_TYPE_C2H_DATA; + if (last_pdu) + c2h.common.flags |= NVME_TCP_C2H_DATA_FLAGS_LAST_PDU; + if (success) + c2h.common.flags |= NVME_TCP_C2H_DATA_FLAGS_SUCCESS; + c2h.cccid = cid; + c2h.datao = htole32(data_offset); + c2h.datal = htole32(len); + + top = nvmf_che_construct_pdu(qp, &c2h, sizeof(c2h), m, len); + nvmf_che_write_pdu(qp, top); +} + +static u_int +che_send_controller_data(struct nvmf_capsule *nc, uint32_t data_offset, + struct mbuf *m, size_t len) +{ + struct nvmf_che_qpair *qp = CQP(nc->nc_qpair); + struct nvme_sgl_descriptor *sgl; + uint32_t data_len; + bool last_pdu, last_xfer; + + if (nc->nc_qe_len != sizeof(struct nvme_command) || + !qp->qp.nq_controller) { + m_freem(m); + return (NVME_SC_INVALID_FIELD); + } + + sgl = &nc->nc_sqe.sgl; + data_len = le32toh(sgl->length); + if (data_offset + len > data_len) { + m_freem(m); + return (NVME_SC_INVALID_FIELD); + } + last_xfer = (data_offset + len == data_len); + + if (sgl->type != NVME_SGL_TYPE_COMMAND_BUFFER) { + m_freem(m); + return (NVME_SC_INVALID_FIELD); + } + + KASSERT(data_offset == CCAP(nc)->tx_data_offset, + ("%s: starting data_offset %u doesn't match end of previous xfer %u", + __func__, data_offset, CCAP(nc)->tx_data_offset)); + + /* Queue one or more C2H_DATA PDUs containing the data from 'm'. */ + while (m != NULL) { + struct mbuf *n; + uint32_t todo; + + if (m->m_len > qp->max_tx_data) { + n = m_split(m, qp->max_tx_data, M_WAITOK); + todo = m->m_len; + } else { + struct mbuf *p; + + todo = m->m_len; + p = m; + n = p->m_next; + while (n != NULL) { + if (todo + n->m_len > qp->max_tx_data) { + p->m_next = NULL; + break; + } + todo += n->m_len; + p = n; + n = p->m_next; + } + MPASS(m_length(m, NULL) == todo); + } + + last_pdu = (n == NULL && last_xfer); + che_send_c2h_pdu(qp, nc->nc_sqe.cid, data_offset, m, todo, + last_pdu, last_pdu && qp->send_success); + + data_offset += todo; + data_len -= todo; + m = n; + } + MPASS(data_len == 0); + +#ifdef INVARIANTS + CCAP(nc)->tx_data_offset = data_offset; +#endif + if (!last_xfer) + return (NVMF_MORE); + else if (qp->send_success) + return (NVMF_SUCCESS_SENT); + else + return (NVME_SC_SUCCESS); +} + +struct nvmf_transport_ops che_ops = { + .allocate_qpair = che_allocate_qpair, + .free_qpair = che_free_qpair, + .max_ioccsz = che_max_ioccsz, + .max_xfer_size = che_max_xfer_size, + .allocate_capsule = che_allocate_capsule, + .free_capsule = che_free_capsule, + .transmit_capsule = che_transmit_capsule, + .validate_command_capsule = che_validate_command_capsule, + .capsule_data_len = che_capsule_data_len, + .receive_controller_data = che_receive_controller_data, + .send_controller_data = che_send_controller_data, + .trtype = NVMF_TRTYPE_TCP, + .priority = 10, +}; + +NVMF_TRANSPORT(che, che_ops); + +static void +read_pdu_limits(struct adapter *sc, u_int *max_tx_pdu_len, + uint32_t *max_rx_pdu_len) +{ + uint32_t tx_len, rx_len, r, v; + + /* Copied from cxgbei, but not sure if this is correct. */ + rx_len = t4_read_reg(sc, A_TP_PMM_RX_PAGE_SIZE); + tx_len = t4_read_reg(sc, A_TP_PMM_TX_PAGE_SIZE); + + r = t4_read_reg(sc, A_TP_PARA_REG2); + rx_len = min(rx_len, G_MAXRXDATA(r)); + tx_len = min(tx_len, G_MAXRXDATA(r)); + + r = t4_read_reg(sc, A_TP_PARA_REG7); + v = min(G_PMMAXXFERLEN0(r), G_PMMAXXFERLEN1(r)); + rx_len = min(rx_len, v); + tx_len = min(tx_len, v); + + /* Cannot be larger than 32KB - 256. */ + rx_len = min(rx_len, 32512); + tx_len = min(tx_len, 32512); + + *max_tx_pdu_len = tx_len; + *max_rx_pdu_len = rx_len; +} + +static int +nvmf_che_init(struct adapter *sc, struct nvmf_che_adapter *nca) +{ + struct sysctl_oid *oid; + struct sysctl_oid_list *children; + uint32_t val; + + read_pdu_limits(sc, &nca->max_transmit_pdu, &nca->max_receive_pdu); + if (nca->max_transmit_pdu > che_max_transmit_pdu) + nca->max_transmit_pdu = che_max_transmit_pdu; + if (nca->max_receive_pdu > che_max_receive_pdu) + nca->max_receive_pdu = che_max_receive_pdu; + val = t4_read_reg(sc, A_SGE_CONTROL2); + nca->nvmt_data_iqe = (val & F_RXCPLMODE_NVMT) != 0; + + sysctl_ctx_init(&nca->ctx); + oid = device_get_sysctl_tree(sc->dev); /* dev.che.X */ + children = SYSCTL_CHILDREN(oid); + + oid = SYSCTL_ADD_NODE(&nca->ctx, children, OID_AUTO, "nvme", + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "NVMe ULP settings"); + children = SYSCTL_CHILDREN(oid); + + nca->ddp_threshold = 8192; + SYSCTL_ADD_UINT(&nca->ctx, children, OID_AUTO, "ddp_threshold", + CTLFLAG_RW, &nca->ddp_threshold, 0, "Rx zero copy threshold"); + + SYSCTL_ADD_UINT(&nca->ctx, children, OID_AUTO, "max_transmit_pdu", + CTLFLAG_RW, &nca->max_transmit_pdu, 0, + "Maximum size of a transmitted PDU"); + + SYSCTL_ADD_UINT(&nca->ctx, children, OID_AUTO, "max_receive_pdu", + CTLFLAG_RW, &nca->max_receive_pdu, 0, + "Maximum size of a received PDU"); + + return (0); +} + +static void +nvmf_che_destroy(struct nvmf_che_adapter *nca) +{ + sysctl_ctx_free(&nca->ctx); + free(nca, M_CXGBE); +} + +static int +nvmf_che_activate(struct adapter *sc) +{ + struct nvmf_che_adapter *nca; + int rc; + + ASSERT_SYNCHRONIZED_OP(sc); + + if (uld_active(sc, ULD_NVME)) { + KASSERT(0, ("%s: NVMe offload already enabled on adapter %p", + __func__, sc)); + return (0); + } + + if ((sc->nvmecaps & FW_CAPS_CONFIG_NVME_TCP) == 0) { + device_printf(sc->dev, + "not NVMe offload capable, or capability disabled\n"); + return (ENOSYS); + } + + /* per-adapter softc for NVMe */ + nca = malloc(sizeof(*nca), M_CXGBE, M_ZERO | M_WAITOK); + nca->sc = sc; + + rc = nvmf_che_init(sc, nca); + if (rc != 0) { + free(nca, M_CXGBE); + return (rc); + } + + sc->nvme_ulp_softc = nca; + + return (0); +} + +static int +nvmf_che_deactivate(struct adapter *sc) +{ + struct nvmf_che_adapter *nca = sc->nvme_ulp_softc; + + ASSERT_SYNCHRONIZED_OP(sc); + + if (nca != NULL) { + nvmf_che_destroy(nca); + sc->nvme_ulp_softc = NULL; + } + + return (0); +} + +static void +nvmf_che_activate_all(struct adapter *sc, void *arg __unused) +{ + if (begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t7nvact") != 0) + return; + + /* Activate NVMe if any port on this adapter has IFCAP_TOE enabled. */ + if (sc->offload_map && !uld_active(sc, ULD_NVME)) + (void) t4_activate_uld(sc, ULD_NVME); + + end_synchronized_op(sc, 0); +} + +static void +nvmf_che_deactivate_all(struct adapter *sc, void *arg __unused) +{ + if (begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t7nvdea") != 0) + return; + + if (uld_active(sc, ULD_NVME)) + (void) t4_deactivate_uld(sc, ULD_NVME); + + end_synchronized_op(sc, 0); +} + +static struct uld_info nvmf_che_uld_info = { + .uld_activate = nvmf_che_activate, + .uld_deactivate = nvmf_che_deactivate, +}; + +static int +nvmf_che_mod_load(void) +{ + int rc; + + t4_register_cpl_handler(CPL_NVMT_CMP, do_nvmt_cmp); + t4_register_cpl_handler(CPL_NVMT_DATA, do_nvmt_data); + + rc = t4_register_uld(&nvmf_che_uld_info, ULD_NVME); + if (rc != 0) + return (rc); + + t4_iterate(nvmf_che_activate_all, NULL); + + return (rc); +} + +static int +nvmf_che_mod_unload(void) +{ + t4_iterate(nvmf_che_deactivate_all, NULL); + + if (t4_unregister_uld(&nvmf_che_uld_info, ULD_NVME) == EBUSY) + return (EBUSY); + + t4_register_cpl_handler(CPL_NVMT_CMP, NULL); + t4_register_cpl_handler(CPL_NVMT_DATA, NULL); + + return (0); +} +#endif + +static int +nvmf_che_modevent(module_t mod, int cmd, void *arg) +{ + int rc; + +#ifdef TCP_OFFLOAD + switch (cmd) { + case MOD_LOAD: + rc = nvmf_che_mod_load(); + break; + case MOD_UNLOAD: + rc = nvmf_che_mod_unload(); + break; + default: + rc = EOPNOTSUPP; + break; + } +#else + printf("nvmf_che: compiled without TCP_OFFLOAD support.\n"); + rc = EOPNOTSUPP; +#endif + + return (rc); +} + +static moduledata_t nvmf_che_mod = { + "nvmf_che", + nvmf_che_modevent, + NULL, +}; + +MODULE_VERSION(nvmf_che, 1); +DECLARE_MODULE(nvmf_che, nvmf_che_mod, SI_SUB_EXEC, SI_ORDER_ANY); +MODULE_DEPEND(nvmf_che, t4_tom, 1, 1, 1); +MODULE_DEPEND(nvmf_che, cxgbe, 1, 1, 1); diff --git a/sys/dev/cxgbe/offload.h b/sys/dev/cxgbe/offload.h index 91a43785aaca..d63accf86e2a 100644 --- a/sys/dev/cxgbe/offload.h +++ b/sys/dev/cxgbe/offload.h @@ -196,7 +196,8 @@ enum { ULD_TOM = 0, ULD_IWARP, ULD_ISCSI, - ULD_MAX = ULD_ISCSI + ULD_NVME, + ULD_MAX = ULD_NVME }; struct adapter; diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index 22d2f504c257..15b3fd94fa54 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -57,9 +57,7 @@ #include <net/if_types.h> #include <net/if_dl.h> #include <net/if_vlan_var.h> -#ifdef RSS #include <net/rss_config.h> -#endif #include <netinet/in.h> #include <netinet/ip.h> #ifdef KERN_TLS @@ -611,7 +609,7 @@ static int t4_switchcaps_allowed = FW_CAPS_CONFIG_SWITCH_INGRESS | SYSCTL_INT(_hw_cxgbe, OID_AUTO, switchcaps_allowed, CTLFLAG_RDTUN, &t4_switchcaps_allowed, 0, "Default switch capabilities"); -static int t4_nvmecaps_allowed = 0; +static int t4_nvmecaps_allowed = -1; SYSCTL_INT(_hw_cxgbe, OID_AUTO, nvmecaps_allowed, CTLFLAG_RDTUN, &t4_nvmecaps_allowed, 0, "Default NVMe capabilities"); @@ -1327,6 +1325,8 @@ t4_attach(device_t dev) sc->dev = dev; sysctl_ctx_init(&sc->ctx); TUNABLE_INT_FETCH("hw.cxgbe.dflags", &sc->debug_flags); + if (TUNABLE_INT_FETCH("hw.cxgbe.iflags", &sc->intr_flags) == 0) + sc->intr_flags = IHF_INTR_CLEAR_ON_INIT | IHF_CLR_ALL_UNIGNORED; if ((pci_get_device(dev) & 0xff00) == 0x5400) t5_attribute_workaround(dev); @@ -3652,6 +3652,7 @@ port_mword(struct port_info *pi, uint32_t speed) case FW_PORT_TYPE_SFP28: case FW_PORT_TYPE_SFP56: case FW_PORT_TYPE_QSFP56: + case FW_PORT_TYPE_QSFPDD: /* Pluggable transceiver */ switch (pi->mod_type) { case FW_PORT_MOD_TYPE_LR: @@ -3671,6 +3672,8 @@ port_mword(struct port_info *pi, uint32_t speed) return (IFM_100G_LR4); case FW_PORT_CAP32_SPEED_200G: return (IFM_200G_LR4); + case FW_PORT_CAP32_SPEED_400G: + return (IFM_400G_LR8); } break; case FW_PORT_MOD_TYPE_SR: @@ -3689,6 +3692,8 @@ port_mword(struct port_info *pi, uint32_t speed) return (IFM_100G_SR4); case FW_PORT_CAP32_SPEED_200G: return (IFM_200G_SR4); + case FW_PORT_CAP32_SPEED_400G: + return (IFM_400G_SR8); } break; case FW_PORT_MOD_TYPE_ER: @@ -3712,6 +3717,8 @@ port_mword(struct port_info *pi, uint32_t speed) return (IFM_100G_CR4); case FW_PORT_CAP32_SPEED_200G: return (IFM_200G_CR4_PAM4); + case FW_PORT_CAP32_SPEED_400G: + return (IFM_400G_CR8); } break; case FW_PORT_MOD_TYPE_LRM: @@ -3723,10 +3730,12 @@ port_mword(struct port_info *pi, uint32_t speed) return (IFM_100G_DR); if (speed == FW_PORT_CAP32_SPEED_200G) return (IFM_200G_DR4); + if (speed == FW_PORT_CAP32_SPEED_400G) + return (IFM_400G_DR4); break; case FW_PORT_MOD_TYPE_NA: MPASS(0); /* Not pluggable? */ - /* fall throough */ + /* fall through */ case FW_PORT_MOD_TYPE_ERROR: case FW_PORT_MOD_TYPE_UNKNOWN: case FW_PORT_MOD_TYPE_NOTSUPPORTED: @@ -3735,6 +3744,10 @@ port_mword(struct port_info *pi, uint32_t speed) return (IFM_NONE); } break; + case M_FW_PORT_CMD_PTYPE: /* FW_PORT_TYPE_NONE for old firmware */ + if (chip_id(pi->adapter) >= CHELSIO_T7) + return (IFM_UNKNOWN); + /* fall through */ case FW_PORT_TYPE_NONE: return (IFM_NONE); } @@ -3930,8 +3943,6 @@ fatal_error_task(void *arg, int pending) void t4_fatal_err(struct adapter *sc, bool fw_error) { - const bool verbose = (sc->debug_flags & DF_VERBOSE_SLOWINTR) != 0; - stop_adapter(sc); if (atomic_testandset_int(&sc->error_flags, ilog2(ADAP_FATAL_ERR))) return; @@ -3944,7 +3955,7 @@ t4_fatal_err(struct adapter *sc, bool fw_error) * main INT_CAUSE registers here to make sure we haven't missed * anything interesting. */ - t4_slow_intr_handler(sc, verbose); + t4_slow_intr_handler(sc, sc->intr_flags); atomic_set_int(&sc->error_flags, ADAP_CIM_ERR); } t4_report_fw_error(sc); @@ -5408,6 +5419,7 @@ apply_cfg_and_initialize(struct adapter *sc, char *cfg_file, caps.toecaps = 0; caps.rdmacaps = 0; caps.iscsicaps = 0; + caps.nvmecaps = 0; } caps.op_to_write = htobe32(V_FW_CMD_OP(FW_CAPS_CONFIG_CMD) | @@ -5881,61 +5893,63 @@ get_params__post_init(struct adapter *sc) * that will never be used. */ sc->iscsicaps = 0; + sc->nvmecaps = 0; sc->rdmacaps = 0; } - if (sc->rdmacaps) { + if (sc->nvmecaps || sc->rdmacaps) { param[0] = FW_PARAM_PFVF(STAG_START); param[1] = FW_PARAM_PFVF(STAG_END); - param[2] = FW_PARAM_PFVF(RQ_START); - param[3] = FW_PARAM_PFVF(RQ_END); - param[4] = FW_PARAM_PFVF(PBL_START); - param[5] = FW_PARAM_PFVF(PBL_END); - rc = -t4_query_params(sc, sc->mbox, sc->pf, 0, 6, param, val); + param[2] = FW_PARAM_PFVF(PBL_START); + param[3] = FW_PARAM_PFVF(PBL_END); + rc = -t4_query_params(sc, sc->mbox, sc->pf, 0, 4, param, val); if (rc != 0) { device_printf(sc->dev, - "failed to query RDMA parameters(1): %d.\n", rc); + "failed to query NVMe/RDMA parameters: %d.\n", rc); return (rc); } sc->vres.stag.start = val[0]; sc->vres.stag.size = val[1] - val[0] + 1; - sc->vres.rq.start = val[2]; - sc->vres.rq.size = val[3] - val[2] + 1; - sc->vres.pbl.start = val[4]; - sc->vres.pbl.size = val[5] - val[4] + 1; - - param[0] = FW_PARAM_PFVF(SQRQ_START); - param[1] = FW_PARAM_PFVF(SQRQ_END); - param[2] = FW_PARAM_PFVF(CQ_START); - param[3] = FW_PARAM_PFVF(CQ_END); - param[4] = FW_PARAM_PFVF(OCQ_START); - param[5] = FW_PARAM_PFVF(OCQ_END); + sc->vres.pbl.start = val[2]; + sc->vres.pbl.size = val[3] - val[2] + 1; + } + if (sc->rdmacaps) { + param[0] = FW_PARAM_PFVF(RQ_START); + param[1] = FW_PARAM_PFVF(RQ_END); + param[2] = FW_PARAM_PFVF(SQRQ_START); + param[3] = FW_PARAM_PFVF(SQRQ_END); + param[4] = FW_PARAM_PFVF(CQ_START); + param[5] = FW_PARAM_PFVF(CQ_END); rc = -t4_query_params(sc, sc->mbox, sc->pf, 0, 6, param, val); if (rc != 0) { device_printf(sc->dev, - "failed to query RDMA parameters(2): %d.\n", rc); + "failed to query RDMA parameters(1): %d.\n", rc); return (rc); } - sc->vres.qp.start = val[0]; - sc->vres.qp.size = val[1] - val[0] + 1; - sc->vres.cq.start = val[2]; - sc->vres.cq.size = val[3] - val[2] + 1; - sc->vres.ocq.start = val[4]; - sc->vres.ocq.size = val[5] - val[4] + 1; - - param[0] = FW_PARAM_PFVF(SRQ_START); - param[1] = FW_PARAM_PFVF(SRQ_END); - param[2] = FW_PARAM_DEV(MAXORDIRD_QP); - param[3] = FW_PARAM_DEV(MAXIRD_ADAPTER); - rc = -t4_query_params(sc, sc->mbox, sc->pf, 0, 4, param, val); + sc->vres.rq.start = val[0]; + sc->vres.rq.size = val[1] - val[0] + 1; + sc->vres.qp.start = val[2]; + sc->vres.qp.size = val[3] - val[2] + 1; + sc->vres.cq.start = val[4]; + sc->vres.cq.size = val[5] - val[4] + 1; + + param[0] = FW_PARAM_PFVF(OCQ_START); + param[1] = FW_PARAM_PFVF(OCQ_END); + param[2] = FW_PARAM_PFVF(SRQ_START); + param[3] = FW_PARAM_PFVF(SRQ_END); + param[4] = FW_PARAM_DEV(MAXORDIRD_QP); + param[5] = FW_PARAM_DEV(MAXIRD_ADAPTER); + rc = -t4_query_params(sc, sc->mbox, sc->pf, 0, 6, param, val); if (rc != 0) { device_printf(sc->dev, - "failed to query RDMA parameters(3): %d.\n", rc); + "failed to query RDMA parameters(2): %d.\n", rc); return (rc); } - sc->vres.srq.start = val[0]; - sc->vres.srq.size = val[1] - val[0] + 1; - sc->params.max_ordird_qp = val[2]; - sc->params.max_ird_adapter = val[3]; + sc->vres.ocq.start = val[0]; + sc->vres.ocq.size = val[1] - val[0] + 1; + sc->vres.srq.start = val[2]; + sc->vres.srq.size = val[3] - val[2] + 1; + sc->params.max_ordird_qp = val[4]; + sc->params.max_ird_adapter = val[5]; } if (sc->iscsicaps) { param[0] = FW_PARAM_PFVF(ISCSI_START); @@ -7019,7 +7033,6 @@ t4_setup_intr_handlers(struct adapter *sc) static void write_global_rss_key(struct adapter *sc) { -#ifdef RSS int i; uint32_t raw_rss_key[RSS_KEYSIZE / sizeof(uint32_t)]; uint32_t rss_key[RSS_KEYSIZE / sizeof(uint32_t)]; @@ -7031,7 +7044,6 @@ write_global_rss_key(struct adapter *sc) rss_key[i] = htobe32(raw_rss_key[nitems(rss_key) - 1 - i]); } t4_write_rss_key(sc, &rss_key[0], -1, 1); -#endif } /* @@ -7111,7 +7123,6 @@ adapter_full_uninit(struct adapter *sc) sc->flags &= ~FULL_INIT_DONE; } -#ifdef RSS #define SUPPORTED_RSS_HASHTYPES (RSS_HASHTYPE_RSS_IPV4 | \ RSS_HASHTYPE_RSS_TCP_IPV4 | RSS_HASHTYPE_RSS_IPV6 | \ RSS_HASHTYPE_RSS_TCP_IPV6 | RSS_HASHTYPE_RSS_UDP_IPV4 | \ @@ -7174,7 +7185,6 @@ hashen_to_hashconfig(int hashen) return (hashconfig); } -#endif /* * Idempotent. @@ -7185,9 +7195,9 @@ vi_full_init(struct vi_info *vi) struct adapter *sc = vi->adapter; struct sge_rxq *rxq; int rc, i, j; + int hashconfig = rss_gethashconfig(); #ifdef RSS int nbuckets = rss_getnumbuckets(); - int hashconfig = rss_gethashconfig(); int extra; #endif @@ -7243,9 +7253,9 @@ vi_full_init(struct vi_info *vi) return (rc); } -#ifdef RSS vi->hashen = hashconfig_to_hashen(hashconfig); +#ifdef RSS /* * We may have had to enable some hashes even though the global config * wants them disabled. This is a potential problem that must be @@ -7279,11 +7289,6 @@ vi_full_init(struct vi_info *vi) CH_ALERT(vi, "UDP/IPv4 4-tuple hashing forced on.\n"); if (extra & RSS_HASHTYPE_RSS_UDP_IPV6) CH_ALERT(vi, "UDP/IPv6 4-tuple hashing forced on.\n"); -#else - vi->hashen = F_FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN | - F_FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN | - F_FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN | - F_FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN | F_FW_RSS_VI_CONFIG_CMD_UDPEN; #endif rc = -t4_config_vi_rss(sc, sc->mbox, vi->viid, vi->hashen, vi->rss[0], 0, 0); @@ -7892,6 +7897,9 @@ t4_sysctls(struct adapter *sc) SYSCTL_ADD_INT(ctx, children, OID_AUTO, "dflags", CTLFLAG_RW, &sc->debug_flags, 0, "flags to enable runtime debugging"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, "iflags", CTLFLAG_RW, + &sc->intr_flags, 0, "flags for the slow interrupt handler"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "tp_version", CTLFLAG_RD, sc->tp_version, 0, "TP microcode version"); @@ -8988,7 +8996,7 @@ sysctl_requested_fec(SYSCTL_HANDLER_ARGS) struct adapter *sc = pi->adapter; struct link_config *lc = &pi->link_cfg; int rc; - int8_t old; + int8_t old = lc->requested_fec; if (req->newptr == NULL) { struct sbuf *sb; @@ -8997,16 +9005,15 @@ sysctl_requested_fec(SYSCTL_HANDLER_ARGS) if (sb == NULL) return (ENOMEM); - sbuf_printf(sb, "%b", lc->requested_fec, t4_fec_bits); + sbuf_printf(sb, "%b", old, t4_fec_bits); rc = sbuf_finish(sb); sbuf_delete(sb); } else { char s[8]; int n; - snprintf(s, sizeof(s), "%d", - lc->requested_fec == FEC_AUTO ? -1 : - lc->requested_fec & (M_FW_PORT_CAP32_FEC | FEC_MODULE)); + snprintf(s, sizeof(s), "%d", old == FEC_AUTO ? -1 : + old & (M_FW_PORT_CAP32_FEC | FEC_MODULE)); rc = sysctl_handle_string(oidp, s, sizeof(s), req); if (rc != 0) @@ -9023,7 +9030,10 @@ sysctl_requested_fec(SYSCTL_HANDLER_ARGS) if (rc) return (rc); PORT_LOCK(pi); - old = lc->requested_fec; + if (lc->requested_fec != old) { + rc = EBUSY; + goto done; + } if (n == FEC_AUTO) lc->requested_fec = FEC_AUTO; else if (n == 0 || n == FEC_NONE) @@ -12984,6 +12994,9 @@ clear_stats(struct adapter *sc, u_int port_id) counter_u64_zero(ofld_txq->tx_iscsi_pdus); counter_u64_zero(ofld_txq->tx_iscsi_octets); counter_u64_zero(ofld_txq->tx_iscsi_iso_wrs); + counter_u64_zero(ofld_txq->tx_nvme_pdus); + counter_u64_zero(ofld_txq->tx_nvme_octets); + counter_u64_zero(ofld_txq->tx_nvme_iso_wrs); counter_u64_zero(ofld_txq->tx_aio_jobs); counter_u64_zero(ofld_txq->tx_aio_octets); counter_u64_zero(ofld_txq->tx_toe_tls_records); @@ -13003,6 +13016,22 @@ clear_stats(struct adapter *sc, u_int port_id) ofld_rxq->rx_iscsi_ddp_octets = 0; ofld_rxq->rx_iscsi_fl_pdus = 0; ofld_rxq->rx_iscsi_fl_octets = 0; + counter_u64_zero( + ofld_rxq->rx_nvme_ddp_setup_ok); + counter_u64_zero( + ofld_rxq->rx_nvme_ddp_setup_no_stag); + counter_u64_zero( + ofld_rxq->rx_nvme_ddp_setup_error); + counter_u64_zero(ofld_rxq->rx_nvme_ddp_pdus); + counter_u64_zero(ofld_rxq->rx_nvme_ddp_octets); + counter_u64_zero(ofld_rxq->rx_nvme_fl_pdus); + counter_u64_zero(ofld_rxq->rx_nvme_fl_octets); + counter_u64_zero( + ofld_rxq->rx_nvme_invalid_headers); + counter_u64_zero( + ofld_rxq->rx_nvme_header_digest_errors); + counter_u64_zero( + ofld_rxq->rx_nvme_data_digest_errors); ofld_rxq->rx_aio_ddp_jobs = 0; ofld_rxq->rx_aio_ddp_octets = 0; ofld_rxq->rx_toe_tls_records = 0; @@ -13409,11 +13438,16 @@ toe_capability(struct vi_info *vi, bool enable) ("%s: TOM activated but flag not set", __func__)); } - /* Activate iWARP and iSCSI too, if the modules are loaded. */ + /* + * Activate iWARP, iSCSI, and NVMe too, if the modules + * are loaded. + */ if (!uld_active(sc, ULD_IWARP)) (void) t4_activate_uld(sc, ULD_IWARP); if (!uld_active(sc, ULD_ISCSI)) (void) t4_activate_uld(sc, ULD_ISCSI); + if (!uld_active(sc, ULD_NVME)) + (void) t4_activate_uld(sc, ULD_NVME); if (pi->uld_vis++ == 0) setbit(&sc->offload_map, pi->port_id); @@ -13694,6 +13728,9 @@ tweak_tunables(void) FW_CAPS_CONFIG_ISCSI_T10DIF; } + if (t4_nvmecaps_allowed == -1) + t4_nvmecaps_allowed = FW_CAPS_CONFIG_NVME_TCP; + if (t4_tmr_idx_ofld < 0 || t4_tmr_idx_ofld >= SGE_NTIMERS) t4_tmr_idx_ofld = TMR_IDX_OFLD; @@ -13705,6 +13742,9 @@ tweak_tunables(void) if (t4_iscsicaps_allowed == -1) t4_iscsicaps_allowed = 0; + + if (t4_nvmecaps_allowed == -1) + t4_nvmecaps_allowed = 0; #endif #ifdef DEV_NETMAP diff --git a/sys/dev/cxgbe/t4_sge.c b/sys/dev/cxgbe/t4_sge.c index 2f9cb1a4ebb5..e9754ace27c2 100644 --- a/sys/dev/cxgbe/t4_sge.c +++ b/sys/dev/cxgbe/t4_sge.c @@ -852,6 +852,11 @@ t4_tweak_chip_settings(struct adapter *sc) /* We use multiple DDP page sizes both in plain-TOE and ISCSI modes. */ m = v = F_TDDPTAGTCB | F_ISCSITAGTCB; + if (sc->nvmecaps != 0) { + /* Request DDP status bit for NVMe PDU completions. */ + m |= F_NVME_TCP_DDP_VAL_EN; + v |= F_NVME_TCP_DDP_VAL_EN; + } t4_set_reg_field(sc, A_ULP_RX_CTL, m, v); m = V_INDICATESIZE(M_INDICATESIZE) | F_REARMDDPOFFSET | @@ -1335,7 +1340,6 @@ t4_intr_err(void *arg) { struct adapter *sc = arg; uint32_t v; - const bool verbose = (sc->debug_flags & DF_VERBOSE_SLOWINTR) != 0; if (atomic_load_int(&sc->error_flags) & ADAP_FATAL_ERR) return; @@ -1346,7 +1350,7 @@ t4_intr_err(void *arg) t4_write_reg(sc, MYPF_REG(A_PL_PF_INT_CAUSE), v); } - if (t4_slow_intr_handler(sc, verbose)) + if (t4_slow_intr_handler(sc, sc->intr_flags)) t4_fatal_err(sc, false); } @@ -4170,6 +4174,20 @@ alloc_ofld_rxq(struct vi_info *vi, struct sge_ofld_rxq *ofld_rxq, int idx, ofld_rxq->rx_iscsi_ddp_setup_ok = counter_u64_alloc(M_WAITOK); ofld_rxq->rx_iscsi_ddp_setup_error = counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_ddp_setup_ok = counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_ddp_setup_no_stag = + counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_ddp_setup_error = + counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_ddp_octets = counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_ddp_pdus = counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_fl_octets = counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_fl_pdus = counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_invalid_headers = counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_header_digest_errors = + counter_u64_alloc(M_WAITOK); + ofld_rxq->rx_nvme_data_digest_errors = + counter_u64_alloc(M_WAITOK); ofld_rxq->ddp_buffer_alloc = counter_u64_alloc(M_WAITOK); ofld_rxq->ddp_buffer_reuse = counter_u64_alloc(M_WAITOK); ofld_rxq->ddp_buffer_free = counter_u64_alloc(M_WAITOK); @@ -4207,6 +4225,16 @@ free_ofld_rxq(struct vi_info *vi, struct sge_ofld_rxq *ofld_rxq) MPASS(!(ofld_rxq->iq.flags & IQ_SW_ALLOCATED)); counter_u64_free(ofld_rxq->rx_iscsi_ddp_setup_ok); counter_u64_free(ofld_rxq->rx_iscsi_ddp_setup_error); + counter_u64_free(ofld_rxq->rx_nvme_ddp_setup_ok); + counter_u64_free(ofld_rxq->rx_nvme_ddp_setup_no_stag); + counter_u64_free(ofld_rxq->rx_nvme_ddp_setup_error); + counter_u64_free(ofld_rxq->rx_nvme_ddp_octets); + counter_u64_free(ofld_rxq->rx_nvme_ddp_pdus); + counter_u64_free(ofld_rxq->rx_nvme_fl_octets); + counter_u64_free(ofld_rxq->rx_nvme_fl_pdus); + counter_u64_free(ofld_rxq->rx_nvme_invalid_headers); + counter_u64_free(ofld_rxq->rx_nvme_header_digest_errors); + counter_u64_free(ofld_rxq->rx_nvme_data_digest_errors); counter_u64_free(ofld_rxq->ddp_buffer_alloc); counter_u64_free(ofld_rxq->ddp_buffer_reuse); counter_u64_free(ofld_rxq->ddp_buffer_free); @@ -4218,12 +4246,12 @@ static void add_ofld_rxq_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *oid, struct sge_ofld_rxq *ofld_rxq) { - struct sysctl_oid_list *children; + struct sysctl_oid_list *children, *top; if (ctx == NULL || oid == NULL) return; - children = SYSCTL_CHILDREN(oid); + top = children = SYSCTL_CHILDREN(oid); SYSCTL_ADD_U64(ctx, children, OID_AUTO, "rx_aio_ddp_jobs", CTLFLAG_RD, &ofld_rxq->rx_aio_ddp_jobs, 0, "# of aio_read(2) jobs completed via DDP"); @@ -4280,6 +4308,41 @@ add_ofld_rxq_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *oid, SYSCTL_ADD_U64(ctx, children, OID_AUTO, "data_digest_errors", CTLFLAG_RD, &ofld_rxq->rx_iscsi_data_digest_errors, 0, "# of PDUs with invalid data digests"); + + oid = SYSCTL_ADD_NODE(ctx, top, OID_AUTO, "nvme", + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "TOE NVMe statistics"); + children = SYSCTL_CHILDREN(oid); + + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "ddp_setup_ok", + CTLFLAG_RD, &ofld_rxq->rx_nvme_ddp_setup_ok, + "# of times DDP buffer was setup successfully"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "ddp_setup_no_stag", + CTLFLAG_RD, &ofld_rxq->rx_nvme_ddp_setup_no_stag, + "# of times STAG was not available for DDP buffer setup"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "ddp_setup_error", + CTLFLAG_RD, &ofld_rxq->rx_nvme_ddp_setup_error, + "# of times DDP buffer setup failed"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "ddp_octets", + CTLFLAG_RD, &ofld_rxq->rx_nvme_ddp_octets, + "# of octets placed directly"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "ddp_pdus", + CTLFLAG_RD, &ofld_rxq->rx_nvme_ddp_pdus, + "# of PDUs with data placed directly"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "fl_octets", + CTLFLAG_RD, &ofld_rxq->rx_nvme_fl_octets, + "# of data octets delivered in freelist"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "fl_pdus", + CTLFLAG_RD, &ofld_rxq->rx_nvme_fl_pdus, + "# of PDUs with data delivered in freelist"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "invalid_headers", + CTLFLAG_RD, &ofld_rxq->rx_nvme_invalid_headers, + "# of PDUs with invalid header field"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "header_digest_errors", + CTLFLAG_RD, &ofld_rxq->rx_nvme_header_digest_errors, + "# of PDUs with invalid header digests"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "data_digest_errors", + CTLFLAG_RD, &ofld_rxq->rx_nvme_data_digest_errors, + "# of PDUs with invalid data digests"); } #endif @@ -4957,6 +5020,9 @@ alloc_ofld_txq(struct vi_info *vi, struct sge_ofld_txq *ofld_txq, int idx) ofld_txq->tx_iscsi_pdus = counter_u64_alloc(M_WAITOK); ofld_txq->tx_iscsi_octets = counter_u64_alloc(M_WAITOK); ofld_txq->tx_iscsi_iso_wrs = counter_u64_alloc(M_WAITOK); + ofld_txq->tx_nvme_pdus = counter_u64_alloc(M_WAITOK); + ofld_txq->tx_nvme_octets = counter_u64_alloc(M_WAITOK); + ofld_txq->tx_nvme_iso_wrs = counter_u64_alloc(M_WAITOK); ofld_txq->tx_aio_jobs = counter_u64_alloc(M_WAITOK); ofld_txq->tx_aio_octets = counter_u64_alloc(M_WAITOK); ofld_txq->tx_toe_tls_records = counter_u64_alloc(M_WAITOK); @@ -5000,6 +5066,9 @@ free_ofld_txq(struct vi_info *vi, struct sge_ofld_txq *ofld_txq) counter_u64_free(ofld_txq->tx_iscsi_pdus); counter_u64_free(ofld_txq->tx_iscsi_octets); counter_u64_free(ofld_txq->tx_iscsi_iso_wrs); + counter_u64_free(ofld_txq->tx_nvme_pdus); + counter_u64_free(ofld_txq->tx_nvme_octets); + counter_u64_free(ofld_txq->tx_nvme_iso_wrs); counter_u64_free(ofld_txq->tx_aio_jobs); counter_u64_free(ofld_txq->tx_aio_octets); counter_u64_free(ofld_txq->tx_toe_tls_records); @@ -5029,6 +5098,15 @@ add_ofld_txq_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *oid, SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "tx_iscsi_iso_wrs", CTLFLAG_RD, &ofld_txq->tx_iscsi_iso_wrs, "# of iSCSI segmentation offload work requests"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "tx_nvme_pdus", + CTLFLAG_RD, &ofld_txq->tx_nvme_pdus, + "# of NVMe PDUs transmitted"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "tx_nvme_octets", + CTLFLAG_RD, &ofld_txq->tx_nvme_octets, + "# of payload octets in transmitted NVMe PDUs"); + SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "tx_nvme_iso_wrs", + CTLFLAG_RD, &ofld_txq->tx_nvme_iso_wrs, + "# of NVMe segmentation offload work requests"); SYSCTL_ADD_COUNTER_U64(ctx, children, OID_AUTO, "tx_aio_jobs", CTLFLAG_RD, &ofld_txq->tx_aio_jobs, "# of zero-copy aio_write(2) jobs transmitted"); diff --git a/sys/dev/cxgbe/tom/t4_cpl_io.c b/sys/dev/cxgbe/tom/t4_cpl_io.c index 84e31efa8b58..5c39ae5fa8f3 100644 --- a/sys/dev/cxgbe/tom/t4_cpl_io.c +++ b/sys/dev/cxgbe/tom/t4_cpl_io.c @@ -66,6 +66,7 @@ #include <vm/vm_page.h> #include <dev/iscsi/iscsi_proto.h> +#include <dev/nvmf/nvmf_proto.h> #include "common/common.h" #include "common/t4_msg.h" @@ -495,6 +496,9 @@ t4_close_conn(struct adapter *sc, struct toepcb *toep) #define MIN_ISO_TX_CREDITS (howmany(sizeof(struct cpl_tx_data_iso), 16)) #define MIN_TX_CREDITS(iso) \ (MIN_OFLD_TX_CREDITS + ((iso) ? MIN_ISO_TX_CREDITS : 0)) +#define MIN_OFLD_TX_V2_CREDITS (howmany(sizeof(struct fw_ofld_tx_data_v2_wr) + 1, 16)) +#define MIN_TX_V2_CREDITS(iso) \ + (MIN_OFLD_TX_V2_CREDITS + ((iso) ? MIN_ISO_TX_CREDITS : 0)) _Static_assert(MAX_OFLD_TX_CREDITS <= MAX_OFLD_TX_SDESC_CREDITS, "MAX_OFLD_TX_SDESC_CREDITS too small"); @@ -542,6 +546,46 @@ max_dsgl_nsegs(int tx_credits, int iso) return (nseg); } +/* Maximum amount of immediate data we could stuff in a WR */ +static inline int +max_imm_payload_v2(int tx_credits, int iso) +{ + const int iso_cpl_size = iso ? sizeof(struct cpl_tx_data_iso) : 0; + + KASSERT(tx_credits >= 0 && + tx_credits <= MAX_OFLD_TX_CREDITS, + ("%s: %d credits", __func__, tx_credits)); + + if (tx_credits < MIN_TX_V2_CREDITS(iso)) + return (0); + + return (tx_credits * 16 - sizeof(struct fw_ofld_tx_data_v2_wr) - + iso_cpl_size); +} + +/* Maximum number of SGL entries we could stuff in a WR */ +static inline int +max_dsgl_nsegs_v2(int tx_credits, int iso, int imm_payload) +{ + int nseg = 1; /* ulptx_sgl has room for 1, rest ulp_tx_sge_pair */ + int sge_pair_credits = tx_credits - MIN_TX_V2_CREDITS(iso); + + KASSERT(tx_credits >= 0 && + tx_credits <= MAX_OFLD_TX_CREDITS, + ("%s: %d credits", __func__, tx_credits)); + + if (tx_credits < MIN_TX_V2_CREDITS(iso) || + sge_pair_credits <= howmany(imm_payload, 16)) + return (0); + sge_pair_credits -= howmany(imm_payload, 16); + + nseg += 2 * (sge_pair_credits * 16 / 24); + if ((sge_pair_credits * 16) % 24 == 16) + nseg++; + + return (nseg); +} + static inline void write_tx_wr(void *dst, struct toepcb *toep, int fw_wr_opcode, unsigned int immdlen, unsigned int plen, uint8_t credits, int shove, @@ -569,6 +613,35 @@ write_tx_wr(void *dst, struct toepcb *toep, int fw_wr_opcode, } } +static inline void +write_tx_v2_wr(void *dst, struct toepcb *toep, int fw_wr_opcode, + unsigned int immdlen, unsigned int plen, uint8_t credits, int shove, + int ulp_submode) +{ + struct fw_ofld_tx_data_v2_wr *txwr = dst; + uint32_t flags; + + memset(txwr, 0, sizeof(*txwr)); + txwr->op_to_immdlen = htobe32(V_WR_OP(fw_wr_opcode) | + V_FW_WR_IMMDLEN(immdlen)); + txwr->flowid_len16 = htobe32(V_FW_WR_FLOWID(toep->tid) | + V_FW_WR_LEN16(credits)); + txwr->plen = htobe32(plen); + flags = V_TX_ULP_MODE(ULP_MODE_NVMET) | V_TX_ULP_SUBMODE(ulp_submode) | + V_TX_URG(0) | V_TX_SHOVE(shove); + + if (toep->params.tx_align > 0) { + if (plen < 2 * toep->params.emss) + flags |= F_FW_OFLD_TX_DATA_WR_LSODISABLE; + else + flags |= F_FW_OFLD_TX_DATA_WR_ALIGNPLD | + (toep->params.nagle == 0 ? 0 : + F_FW_OFLD_TX_DATA_WR_ALIGNPLDSHOVE); + } + + txwr->lsodisable_to_flags = htobe32(flags); +} + /* * Generate a DSGL from a starting mbuf. The total number of segments and the * maximum segments in any one mbuf are provided. @@ -982,8 +1055,8 @@ rqdrop_locked(struct mbufq *q, int plen) #define ULP_ISO G_TX_ULP_SUBMODE(F_FW_ISCSI_TX_DATA_WR_ULPSUBMODE_ISO) static void -write_tx_data_iso(void *dst, u_int ulp_submode, uint8_t flags, uint16_t mss, - int len, int npdu) +write_iscsi_tx_data_iso(void *dst, u_int ulp_submode, uint8_t flags, + uint16_t mss, int len, int npdu) { struct cpl_tx_data_iso *cpl; unsigned int burst_size; @@ -1147,7 +1220,7 @@ write_iscsi_mbuf_wr(struct toepcb *toep, struct mbuf *sndptr) adjusted_plen, credits, shove, ulp_submode | ULP_ISO); cpl_iso = (struct cpl_tx_data_iso *)(txwr + 1); MPASS(plen == sndptr->m_pkthdr.len); - write_tx_data_iso(cpl_iso, ulp_submode, + write_iscsi_tx_data_iso(cpl_iso, ulp_submode, mbuf_iscsi_iso_flags(sndptr), iso_mss, plen, npdu); p = cpl_iso + 1; } else { @@ -1183,21 +1256,269 @@ write_iscsi_mbuf_wr(struct toepcb *toep, struct mbuf *sndptr) return (wr); } +static void +write_nvme_tx_data_iso(void *dst, u_int ulp_submode, u_int iso_type, + uint16_t mss, int len, int npdu, int pdo) +{ + struct cpl_t7_tx_data_iso *cpl; + unsigned int burst_size; + + /* + * TODO: Need to figure out how the LAST_PDU and SUCCESS flags + * are handled. + * + * - Does len need padding bytes? (If so, does padding need + * to be in DSGL input?) + * + * - burst always 0? + */ + burst_size = 0; + + cpl = (struct cpl_t7_tx_data_iso *)dst; + cpl->op_to_scsi = htonl(V_CPL_T7_TX_DATA_ISO_OPCODE(CPL_TX_DATA_ISO) | + V_CPL_T7_TX_DATA_ISO_FIRST(1) | + V_CPL_T7_TX_DATA_ISO_LAST(1) | + V_CPL_T7_TX_DATA_ISO_CPLHDRLEN(0) | + V_CPL_T7_TX_DATA_ISO_HDRCRC(!!(ulp_submode & ULP_CRC_HEADER)) | + V_CPL_T7_TX_DATA_ISO_PLDCRC(!!(ulp_submode & ULP_CRC_DATA)) | + V_CPL_T7_TX_DATA_ISO_IMMEDIATE(0) | + V_CPL_T7_TX_DATA_ISO_SCSI(iso_type)); + + cpl->nvme_tcp_pkd = F_CPL_T7_TX_DATA_ISO_NVME_TCP; + cpl->ahs = 0; + cpl->mpdu = htons(DIV_ROUND_UP(mss, 4)); + cpl->burst = htonl(DIV_ROUND_UP(burst_size, 4)); + cpl->size = htonl(len); + cpl->num_pi_bytes_seglen_offset = htonl(0); + cpl->datasn_offset = htonl(0); + cpl->buffer_offset = htonl(0); + cpl->pdo_pkd = pdo; +} + +static struct wrqe * +write_nvme_mbuf_wr(struct toepcb *toep, struct mbuf *sndptr) +{ + struct mbuf *m; + const struct nvme_tcp_common_pdu_hdr *hdr; + struct fw_v2_nvmet_tx_data_wr *txwr; + struct cpl_tx_data_iso *cpl_iso; + void *p; + struct wrqe *wr; + u_int plen, nsegs, credits, max_imm, max_nsegs, max_nsegs_1mbuf; + u_int adjusted_plen, imm_data, ulp_submode; + struct inpcb *inp = toep->inp; + struct tcpcb *tp = intotcpcb(inp); + int tx_credits, shove, npdu, wr_len; + uint16_t iso_mss; + bool iso, nomap_mbuf_seen; + + M_ASSERTPKTHDR(sndptr); + + tx_credits = min(toep->tx_credits, MAX_OFLD_TX_CREDITS); + if (mbuf_raw_wr(sndptr)) { + plen = sndptr->m_pkthdr.len; + KASSERT(plen <= SGE_MAX_WR_LEN, + ("raw WR len %u is greater than max WR len", plen)); + if (plen > tx_credits * 16) + return (NULL); + + wr = alloc_wrqe(roundup2(plen, 16), &toep->ofld_txq->wrq); + if (__predict_false(wr == NULL)) + return (NULL); + + m_copydata(sndptr, 0, plen, wrtod(wr)); + return (wr); + } + + /* + * The first mbuf is the PDU header that is always sent as + * immediate data. + */ + imm_data = sndptr->m_len; + + iso = mbuf_iscsi_iso(sndptr); + max_imm = max_imm_payload_v2(tx_credits, iso); + + /* + * Not enough credits for the PDU header. + */ + if (imm_data > max_imm) + return (NULL); + + max_nsegs = max_dsgl_nsegs_v2(tx_credits, iso, imm_data); + iso_mss = mbuf_iscsi_iso_mss(sndptr); + + plen = imm_data; + nsegs = 0; + max_nsegs_1mbuf = 0; /* max # of SGL segments in any one mbuf */ + nomap_mbuf_seen = false; + for (m = sndptr->m_next; m != NULL; m = m->m_next) { + int n; + + if (m->m_flags & M_EXTPG) + n = sglist_count_mbuf_epg(m, mtod(m, vm_offset_t), + m->m_len); + else + n = sglist_count(mtod(m, void *), m->m_len); + + nsegs += n; + plen += m->m_len; + + /* + * This mbuf would send us _over_ the nsegs limit. + * Suspend tx because the PDU can't be sent out. + */ + if ((nomap_mbuf_seen || plen > max_imm) && nsegs > max_nsegs) + return (NULL); + + if (m->m_flags & M_EXTPG) + nomap_mbuf_seen = true; + if (max_nsegs_1mbuf < n) + max_nsegs_1mbuf = n; + } + + if (__predict_false(toep->flags & TPF_FIN_SENT)) + panic("%s: excess tx.", __func__); + + /* + * We have a PDU to send. All of it goes out in one WR so 'm' + * is NULL. A PDU's length is always a multiple of 4. + */ + MPASS(m == NULL); + MPASS((plen & 3) == 0); + MPASS(sndptr->m_pkthdr.len == plen); + + shove = !(tp->t_flags & TF_MORETOCOME); + + /* + * plen doesn't include header digests, padding, and data + * digests which are generated and inserted in the right + * places by the TOE, but they do occupy TCP sequence space + * and need to be accounted for. + * + * To determine the overhead, check the PDU header in sndptr. + * Note that only certain PDU types can use digests and + * padding, and PDO accounts for all but the data digests for + * those PDUs. + */ + MPASS((sndptr->m_flags & M_EXTPG) == 0); + ulp_submode = mbuf_ulp_submode(sndptr); + hdr = mtod(sndptr, const void *); + switch (hdr->pdu_type) { + case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: + case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: + MPASS(ulp_submode == 0); + MPASS(!iso); + break; + case NVME_TCP_PDU_TYPE_CAPSULE_RESP: + case NVME_TCP_PDU_TYPE_R2T: + MPASS((ulp_submode & ULP_CRC_DATA) == 0); + /* FALLTHROUGH */ + case NVME_TCP_PDU_TYPE_CAPSULE_CMD: + MPASS(!iso); + break; + case NVME_TCP_PDU_TYPE_H2C_DATA: + case NVME_TCP_PDU_TYPE_C2H_DATA: + if (le32toh(hdr->plen) + ((ulp_submode & ULP_CRC_DATA) != 0 ? + sizeof(uint32_t) : 0) == plen) + MPASS(!iso); + break; + default: + __assert_unreachable(); + } + + if (iso) { + npdu = howmany(plen - hdr->hlen, iso_mss); + adjusted_plen = hdr->pdo * npdu + (plen - hdr->hlen); + if ((ulp_submode & ULP_CRC_DATA) != 0) + adjusted_plen += npdu * sizeof(uint32_t); + } else { + npdu = 1; + adjusted_plen = le32toh(hdr->plen); + } + wr_len = sizeof(*txwr); + if (iso) + wr_len += sizeof(struct cpl_tx_data_iso); + if (plen <= max_imm && !nomap_mbuf_seen) { + /* Immediate data tx for full PDU */ + imm_data = plen; + wr_len += plen; + nsegs = 0; + } else { + /* DSGL tx for PDU data */ + wr_len += roundup2(imm_data, 16); + wr_len += sizeof(struct ulptx_sgl) + + ((3 * (nsegs - 1)) / 2 + ((nsegs - 1) & 1)) * 8; + } + + wr = alloc_wrqe(roundup2(wr_len, 16), &toep->ofld_txq->wrq); + if (wr == NULL) { + /* XXX: how will we recover from this? */ + return (NULL); + } + txwr = wrtod(wr); + credits = howmany(wr->wr_len, 16); + + if (iso) { + write_tx_v2_wr(txwr, toep, FW_V2_NVMET_TX_DATA_WR, + imm_data + sizeof(struct cpl_tx_data_iso), + adjusted_plen, credits, shove, ulp_submode | ULP_ISO); + cpl_iso = (struct cpl_tx_data_iso *)(txwr + 1); + MPASS(plen == sndptr->m_pkthdr.len); + write_nvme_tx_data_iso(cpl_iso, ulp_submode, + (hdr->pdu_type & 0x1) == 0 ? 1 : 2, iso_mss, plen, npdu, + hdr->pdo); + p = cpl_iso + 1; + } else { + write_tx_v2_wr(txwr, toep, FW_V2_NVMET_TX_DATA_WR, imm_data, + adjusted_plen, credits, shove, ulp_submode); + p = txwr + 1; + } + + /* PDU header (and immediate data payload). */ + m_copydata(sndptr, 0, imm_data, p); + if (nsegs != 0) { + p = roundup2((char *)p + imm_data, 16); + write_tx_sgl(p, sndptr->m_next, NULL, nsegs, max_nsegs_1mbuf); + if (wr_len & 0xf) { + uint64_t *pad = (uint64_t *)((uintptr_t)txwr + wr_len); + *pad = 0; + } + } + + KASSERT(toep->tx_credits >= credits, + ("%s: not enough credits: credits %u " + "toep->tx_credits %u tx_credits %u nsegs %u " + "max_nsegs %u iso %d", __func__, credits, + toep->tx_credits, tx_credits, nsegs, max_nsegs, iso)); + + tp->snd_nxt += adjusted_plen; + tp->snd_max += adjusted_plen; + + counter_u64_add(toep->ofld_txq->tx_nvme_pdus, npdu); + counter_u64_add(toep->ofld_txq->tx_nvme_octets, plen); + if (iso) + counter_u64_add(toep->ofld_txq->tx_nvme_iso_wrs, 1); + + return (wr); +} + void t4_push_pdus(struct adapter *sc, struct toepcb *toep, int drop) { struct mbuf *sndptr, *m; struct fw_wr_hdr *wrhdr; struct wrqe *wr; - u_int plen, credits; + u_int plen, credits, mode; struct inpcb *inp = toep->inp; struct ofld_tx_sdesc *txsd = &toep->txsd[toep->txsd_pidx]; struct mbufq *pduq = &toep->ulp_pduq; INP_WLOCK_ASSERT(inp); + mode = ulp_mode(toep); KASSERT(toep->flags & TPF_FLOWC_WR_SENT, ("%s: flowc_wr not sent for tid %u.", __func__, toep->tid)); - KASSERT(ulp_mode(toep) == ULP_MODE_ISCSI, + KASSERT(mode == ULP_MODE_ISCSI || mode == ULP_MODE_NVMET, ("%s: ulp_mode %u for toep %p", __func__, ulp_mode(toep), toep)); if (__predict_false(toep->flags & TPF_ABORT_SHUTDOWN)) @@ -1230,7 +1551,7 @@ t4_push_pdus(struct adapter *sc, struct toepcb *toep, int drop) if (sbu > 0) { /* * The data transmitted before the - * tid's ULP mode changed to ISCSI is + * tid's ULP mode changed to ISCSI/NVMET is * still in so_snd. Incoming credits * should account for so_snd first. */ @@ -1243,7 +1564,10 @@ t4_push_pdus(struct adapter *sc, struct toepcb *toep, int drop) } while ((sndptr = mbufq_first(pduq)) != NULL) { - wr = write_iscsi_mbuf_wr(toep, sndptr); + if (mode == ULP_MODE_ISCSI) + wr = write_iscsi_mbuf_wr(toep, sndptr); + else + wr = write_nvme_mbuf_wr(toep, sndptr); if (wr == NULL) { toep->flags |= TPF_TX_SUSPENDED; return; @@ -1302,7 +1626,8 @@ static inline void t4_push_data(struct adapter *sc, struct toepcb *toep, int drop) { - if (ulp_mode(toep) == ULP_MODE_ISCSI) + if (ulp_mode(toep) == ULP_MODE_ISCSI || + ulp_mode(toep) == ULP_MODE_NVMET) t4_push_pdus(sc, toep, drop); else if (toep->flags & TPF_KTLS) t4_push_ktls(sc, toep, drop); @@ -1462,7 +1787,8 @@ do_peer_close(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) socantrcvmore(so); if (ulp_mode(toep) == ULP_MODE_RDMA || - (ulp_mode(toep) == ULP_MODE_ISCSI && chip_id(sc) >= CHELSIO_T6)) { + (ulp_mode(toep) == ULP_MODE_ISCSI && chip_id(sc) >= CHELSIO_T6) || + ulp_mode(toep) == ULP_MODE_NVMET) { /* * There might be data received via DDP before the FIN * not reported to the driver. Just assume the @@ -2008,7 +2334,8 @@ do_fw4_ack(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) SOCKBUF_LOCK(sb); sbu = sbused(sb); - if (ulp_mode(toep) == ULP_MODE_ISCSI) { + if (ulp_mode(toep) == ULP_MODE_ISCSI || + ulp_mode(toep) == ULP_MODE_NVMET) { if (__predict_false(sbu > 0)) { /* * The data transmitted before the diff --git a/sys/dev/cxgbe/tom/t4_tom.c b/sys/dev/cxgbe/tom/t4_tom.c index 53a945f8b4cc..8dfffd465345 100644 --- a/sys/dev/cxgbe/tom/t4_tom.c +++ b/sys/dev/cxgbe/tom/t4_tom.c @@ -1990,8 +1990,10 @@ t4_tom_deactivate(struct adapter *sc) if (td == NULL) return (0); /* XXX. KASSERT? */ - if (uld_active(sc, ULD_IWARP) || uld_active(sc, ULD_ISCSI)) - return (EBUSY); /* both iWARP and iSCSI rely on the TOE. */ + /* These ULDs rely on the TOE. */ + if (uld_active(sc, ULD_IWARP) || uld_active(sc, ULD_ISCSI) || + uld_active(sc, ULD_NVME)) + return (EBUSY); if (sc->offload_map != 0) { for_each_port(sc, i) { |
