162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * cxgb3i_offload.c: Chelsio S3xx iscsi offloaded tcp connection management 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2003-2015 Chelsio Communications. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but WITHOUT 762306a36Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 862306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this 962306a36Sopenharmony_ci * release for licensing terms and conditions. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Written by: Dimitris Michailidis (dm@chelsio.com) 1262306a36Sopenharmony_ci * Karen Xie (kxie@chelsio.com) 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/moduleparam.h> 1962306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "common.h" 2262306a36Sopenharmony_ci#include "t3_cpl.h" 2362306a36Sopenharmony_ci#include "t3cdev.h" 2462306a36Sopenharmony_ci#include "cxgb3_defs.h" 2562306a36Sopenharmony_ci#include "cxgb3_ctl_defs.h" 2662306a36Sopenharmony_ci#include "cxgb3_offload.h" 2762306a36Sopenharmony_ci#include "firmware_exports.h" 2862306a36Sopenharmony_ci#include "cxgb3i.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic unsigned int dbg_level; 3162306a36Sopenharmony_ci#include "../libcxgbi.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define DRV_MODULE_NAME "cxgb3i" 3462306a36Sopenharmony_ci#define DRV_MODULE_DESC "Chelsio T3 iSCSI Driver" 3562306a36Sopenharmony_ci#define DRV_MODULE_VERSION "2.0.1-ko" 3662306a36Sopenharmony_ci#define DRV_MODULE_RELDATE "Apr. 2015" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic char version[] = 3962306a36Sopenharmony_ci DRV_MODULE_DESC " " DRV_MODULE_NAME 4062306a36Sopenharmony_ci " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications, Inc."); 4362306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_MODULE_DESC); 4462306a36Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION); 4562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cimodule_param(dbg_level, uint, 0644); 4862306a36Sopenharmony_ciMODULE_PARM_DESC(dbg_level, "debug flag (default=0)"); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int cxgb3i_rcv_win = 256 * 1024; 5162306a36Sopenharmony_cimodule_param(cxgb3i_rcv_win, int, 0644); 5262306a36Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_rcv_win, "TCP receive window in bytes (default=256KB)"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int cxgb3i_snd_win = 128 * 1024; 5562306a36Sopenharmony_cimodule_param(cxgb3i_snd_win, int, 0644); 5662306a36Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_snd_win, "TCP send window in bytes (default=128KB)"); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int cxgb3i_rx_credit_thres = 10 * 1024; 5962306a36Sopenharmony_cimodule_param(cxgb3i_rx_credit_thres, int, 0644); 6062306a36Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_rx_credit_thres, 6162306a36Sopenharmony_ci "RX credits return threshold in bytes (default=10KB)"); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic unsigned int cxgb3i_max_connect = 8 * 1024; 6462306a36Sopenharmony_cimodule_param(cxgb3i_max_connect, uint, 0644); 6562306a36Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_max_connect, "Max. # of connections (default=8092)"); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic unsigned int cxgb3i_sport_base = 20000; 6862306a36Sopenharmony_cimodule_param(cxgb3i_sport_base, uint, 0644); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_sport_base, "starting port number (default=20000)"); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void cxgb3i_dev_open(struct t3cdev *); 7262306a36Sopenharmony_cistatic void cxgb3i_dev_close(struct t3cdev *); 7362306a36Sopenharmony_cistatic void cxgb3i_dev_event_handler(struct t3cdev *, u32, u32); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic struct cxgb3_client t3_client = { 7662306a36Sopenharmony_ci .name = DRV_MODULE_NAME, 7762306a36Sopenharmony_ci .handlers = cxgb3i_cpl_handlers, 7862306a36Sopenharmony_ci .add = cxgb3i_dev_open, 7962306a36Sopenharmony_ci .remove = cxgb3i_dev_close, 8062306a36Sopenharmony_ci .event_handler = cxgb3i_dev_event_handler, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct scsi_host_template cxgb3i_host_template = { 8462306a36Sopenharmony_ci .module = THIS_MODULE, 8562306a36Sopenharmony_ci .name = DRV_MODULE_NAME, 8662306a36Sopenharmony_ci .proc_name = DRV_MODULE_NAME, 8762306a36Sopenharmony_ci .can_queue = CXGB3I_SCSI_HOST_QDEPTH, 8862306a36Sopenharmony_ci .queuecommand = iscsi_queuecommand, 8962306a36Sopenharmony_ci .change_queue_depth = scsi_change_queue_depth, 9062306a36Sopenharmony_ci .sg_tablesize = SG_ALL, 9162306a36Sopenharmony_ci .max_sectors = 0xFFFF, 9262306a36Sopenharmony_ci .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, 9362306a36Sopenharmony_ci .eh_timed_out = iscsi_eh_cmd_timed_out, 9462306a36Sopenharmony_ci .eh_abort_handler = iscsi_eh_abort, 9562306a36Sopenharmony_ci .eh_device_reset_handler = iscsi_eh_device_reset, 9662306a36Sopenharmony_ci .eh_target_reset_handler = iscsi_eh_recover_target, 9762306a36Sopenharmony_ci .target_alloc = iscsi_target_alloc, 9862306a36Sopenharmony_ci .dma_boundary = PAGE_SIZE - 1, 9962306a36Sopenharmony_ci .this_id = -1, 10062306a36Sopenharmony_ci .track_queue_depth = 1, 10162306a36Sopenharmony_ci .cmd_size = sizeof(struct iscsi_cmd), 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic struct iscsi_transport cxgb3i_iscsi_transport = { 10562306a36Sopenharmony_ci .owner = THIS_MODULE, 10662306a36Sopenharmony_ci .name = DRV_MODULE_NAME, 10762306a36Sopenharmony_ci /* owner and name should be set already */ 10862306a36Sopenharmony_ci .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST 10962306a36Sopenharmony_ci | CAP_DATADGST | CAP_DIGEST_OFFLOAD | 11062306a36Sopenharmony_ci CAP_PADDING_OFFLOAD | CAP_TEXT_NEGO, 11162306a36Sopenharmony_ci .attr_is_visible = cxgbi_attr_is_visible, 11262306a36Sopenharmony_ci .get_host_param = cxgbi_get_host_param, 11362306a36Sopenharmony_ci .set_host_param = cxgbi_set_host_param, 11462306a36Sopenharmony_ci /* session management */ 11562306a36Sopenharmony_ci .create_session = cxgbi_create_session, 11662306a36Sopenharmony_ci .destroy_session = cxgbi_destroy_session, 11762306a36Sopenharmony_ci .get_session_param = iscsi_session_get_param, 11862306a36Sopenharmony_ci /* connection management */ 11962306a36Sopenharmony_ci .create_conn = cxgbi_create_conn, 12062306a36Sopenharmony_ci .bind_conn = cxgbi_bind_conn, 12162306a36Sopenharmony_ci .unbind_conn = iscsi_conn_unbind, 12262306a36Sopenharmony_ci .destroy_conn = iscsi_tcp_conn_teardown, 12362306a36Sopenharmony_ci .start_conn = iscsi_conn_start, 12462306a36Sopenharmony_ci .stop_conn = iscsi_conn_stop, 12562306a36Sopenharmony_ci .get_conn_param = iscsi_conn_get_param, 12662306a36Sopenharmony_ci .set_param = cxgbi_set_conn_param, 12762306a36Sopenharmony_ci .get_stats = cxgbi_get_conn_stats, 12862306a36Sopenharmony_ci /* pdu xmit req from user space */ 12962306a36Sopenharmony_ci .send_pdu = iscsi_conn_send_pdu, 13062306a36Sopenharmony_ci /* task */ 13162306a36Sopenharmony_ci .init_task = iscsi_tcp_task_init, 13262306a36Sopenharmony_ci .xmit_task = iscsi_tcp_task_xmit, 13362306a36Sopenharmony_ci .cleanup_task = cxgbi_cleanup_task, 13462306a36Sopenharmony_ci /* pdu */ 13562306a36Sopenharmony_ci .alloc_pdu = cxgbi_conn_alloc_pdu, 13662306a36Sopenharmony_ci .init_pdu = cxgbi_conn_init_pdu, 13762306a36Sopenharmony_ci .xmit_pdu = cxgbi_conn_xmit_pdu, 13862306a36Sopenharmony_ci .parse_pdu_itt = cxgbi_parse_pdu_itt, 13962306a36Sopenharmony_ci /* TCP connect/disconnect */ 14062306a36Sopenharmony_ci .get_ep_param = cxgbi_get_ep_param, 14162306a36Sopenharmony_ci .ep_connect = cxgbi_ep_connect, 14262306a36Sopenharmony_ci .ep_poll = cxgbi_ep_poll, 14362306a36Sopenharmony_ci .ep_disconnect = cxgbi_ep_disconnect, 14462306a36Sopenharmony_ci /* Error recovery timeout call */ 14562306a36Sopenharmony_ci .session_recovery_timedout = iscsi_session_recovery_timedout, 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct scsi_transport_template *cxgb3i_stt; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * CPL (Chelsio Protocol Language) defines a message passing interface between 15262306a36Sopenharmony_ci * the host driver and Chelsio asic. 15362306a36Sopenharmony_ci * The section below implments CPLs that related to iscsi tcp connection 15462306a36Sopenharmony_ci * open/close/abort and data send/receive. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int push_tx_frames(struct cxgbi_sock *csk, int req_completion); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb, 16062306a36Sopenharmony_ci const struct l2t_entry *e) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci unsigned int wscale = cxgbi_sock_compute_wscale(csk->rcv_win); 16362306a36Sopenharmony_ci struct cpl_act_open_req *req = (struct cpl_act_open_req *)skb->head; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_SETUP; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 16862306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ACT_OPEN_REQ, csk->atid)); 16962306a36Sopenharmony_ci req->local_port = csk->saddr.sin_port; 17062306a36Sopenharmony_ci req->peer_port = csk->daddr.sin_port; 17162306a36Sopenharmony_ci req->local_ip = csk->saddr.sin_addr.s_addr; 17262306a36Sopenharmony_ci req->peer_ip = csk->daddr.sin_addr.s_addr; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci req->opt0h = htonl(V_KEEP_ALIVE(1) | F_TCAM_BYPASS | 17562306a36Sopenharmony_ci V_WND_SCALE(wscale) | V_MSS_IDX(csk->mss_idx) | 17662306a36Sopenharmony_ci V_L2T_IDX(e->idx) | V_TX_CHANNEL(e->smt_idx)); 17762306a36Sopenharmony_ci req->opt0l = htonl(V_ULP_MODE(ULP2_MODE_ISCSI) | 17862306a36Sopenharmony_ci V_RCV_BUFSIZ(csk->rcv_win >> 10)); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 18162306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, %pI4:%u-%pI4:%u, %u,%u,%u.\n", 18262306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->atid, 18362306a36Sopenharmony_ci &req->local_ip, ntohs(req->local_port), 18462306a36Sopenharmony_ci &req->peer_ip, ntohs(req->peer_port), 18562306a36Sopenharmony_ci csk->mss_idx, e->idx, e->smt_idx); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci l2t_send(csk->cdev->lldev, skb, csk->l2t); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic inline void act_open_arp_failure(struct t3cdev *dev, struct sk_buff *skb) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci cxgbi_sock_act_open_req_arp_failure(NULL, skb); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * CPL connection close request: host -> 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * Close a connection by sending a CPL_CLOSE_CON_REQ message and queue it to 19962306a36Sopenharmony_ci * the write queue (i.e., after any unsent txt data). 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_cistatic void send_close_req(struct cxgbi_sock *csk) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct sk_buff *skb = csk->cpl_close; 20462306a36Sopenharmony_ci struct cpl_close_con_req *req = (struct cpl_close_con_req *)skb->head; 20562306a36Sopenharmony_ci unsigned int tid = csk->tid; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 20862306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u.\n", 20962306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci csk->cpl_close = NULL; 21262306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_CLOSE_CON)); 21362306a36Sopenharmony_ci req->wr.wr_lo = htonl(V_WR_TID(tid)); 21462306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_CON_REQ, tid)); 21562306a36Sopenharmony_ci req->rsvd = htonl(csk->write_seq); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci cxgbi_sock_skb_entail(csk, skb); 21862306a36Sopenharmony_ci if (csk->state >= CTP_ESTABLISHED) 21962306a36Sopenharmony_ci push_tx_frames(csk, 1); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* 22362306a36Sopenharmony_ci * CPL connection abort request: host -> 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * Send an ABORT_REQ message. Makes sure we do not send multiple ABORT_REQs 22662306a36Sopenharmony_ci * for the same connection and also that we do not try to send a message 22762306a36Sopenharmony_ci * after the connection has closed. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic void abort_arp_failure(struct t3cdev *tdev, struct sk_buff *skb) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct cpl_abort_req *req = cplhdr(skb); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 23462306a36Sopenharmony_ci "t3dev 0x%p, tid %u, skb 0x%p.\n", 23562306a36Sopenharmony_ci tdev, GET_TID(req), skb); 23662306a36Sopenharmony_ci req->cmd = CPL_ABORT_NO_RST; 23762306a36Sopenharmony_ci cxgb3_ofld_send(tdev, skb); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic void send_abort_req(struct cxgbi_sock *csk) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct sk_buff *skb = csk->cpl_abort_req; 24362306a36Sopenharmony_ci struct cpl_abort_req *req; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (unlikely(csk->state == CTP_ABORTING || !skb)) 24662306a36Sopenharmony_ci return; 24762306a36Sopenharmony_ci cxgbi_sock_set_state(csk, CTP_ABORTING); 24862306a36Sopenharmony_ci cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_PENDING); 24962306a36Sopenharmony_ci /* Purge the send queue so we don't send anything after an abort. */ 25062306a36Sopenharmony_ci cxgbi_sock_purge_write_queue(csk); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci csk->cpl_abort_req = NULL; 25362306a36Sopenharmony_ci req = (struct cpl_abort_req *)skb->head; 25462306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_DATA; 25562306a36Sopenharmony_ci set_arp_failure_handler(skb, abort_arp_failure); 25662306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_REQ)); 25762306a36Sopenharmony_ci req->wr.wr_lo = htonl(V_WR_TID(csk->tid)); 25862306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ABORT_REQ, csk->tid)); 25962306a36Sopenharmony_ci req->rsvd0 = htonl(csk->snd_nxt); 26062306a36Sopenharmony_ci req->rsvd1 = !cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT); 26162306a36Sopenharmony_ci req->cmd = CPL_ABORT_SEND_RST; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 26462306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, snd_nxt %u, 0x%x.\n", 26562306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid, csk->snd_nxt, 26662306a36Sopenharmony_ci req->rsvd1); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci l2t_send(csk->cdev->lldev, skb, csk->l2t); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * CPL connection abort reply: host -> 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci * Send an ABORT_RPL message in response of the ABORT_REQ received. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic void send_abort_rpl(struct cxgbi_sock *csk, int rst_status) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct sk_buff *skb = csk->cpl_abort_rpl; 27962306a36Sopenharmony_ci struct cpl_abort_rpl *rpl = (struct cpl_abort_rpl *)skb->head; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 28262306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, status %d.\n", 28362306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid, rst_status); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci csk->cpl_abort_rpl = NULL; 28662306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_DATA; 28762306a36Sopenharmony_ci rpl->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL)); 28862306a36Sopenharmony_ci rpl->wr.wr_lo = htonl(V_WR_TID(csk->tid)); 28962306a36Sopenharmony_ci OPCODE_TID(rpl) = htonl(MK_OPCODE_TID(CPL_ABORT_RPL, csk->tid)); 29062306a36Sopenharmony_ci rpl->cmd = rst_status; 29162306a36Sopenharmony_ci cxgb3_ofld_send(csk->cdev->lldev, skb); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * CPL connection rx data ack: host -> 29662306a36Sopenharmony_ci * Send RX credits through an RX_DATA_ACK CPL message. Returns the number of 29762306a36Sopenharmony_ci * credits sent. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_cistatic u32 send_rx_credits(struct cxgbi_sock *csk, u32 credits) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct sk_buff *skb; 30262306a36Sopenharmony_ci struct cpl_rx_data_ack *req; 30362306a36Sopenharmony_ci u32 dack = F_RX_DACK_CHANGE | V_RX_DACK_MODE(1); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX, 30662306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, credit %u, dack %u.\n", 30762306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid, credits, dack); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci skb = alloc_wr(sizeof(*req), 0, GFP_ATOMIC); 31062306a36Sopenharmony_ci if (!skb) { 31162306a36Sopenharmony_ci pr_info("csk 0x%p, credit %u, OOM.\n", csk, credits); 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci req = (struct cpl_rx_data_ack *)skb->head; 31562306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 31662306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_RX_DATA_ACK, csk->tid)); 31762306a36Sopenharmony_ci req->credit_dack = htonl(F_RX_DACK_CHANGE | V_RX_DACK_MODE(1) | 31862306a36Sopenharmony_ci V_RX_CREDITS(credits)); 31962306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_ACK; 32062306a36Sopenharmony_ci cxgb3_ofld_send(csk->cdev->lldev, skb); 32162306a36Sopenharmony_ci return credits; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* 32562306a36Sopenharmony_ci * CPL connection tx data: host -> 32662306a36Sopenharmony_ci * 32762306a36Sopenharmony_ci * Send iscsi PDU via TX_DATA CPL message. Returns the number of 32862306a36Sopenharmony_ci * credits sent. 32962306a36Sopenharmony_ci * Each TX_DATA consumes work request credit (wrs), so we need to keep track of 33062306a36Sopenharmony_ci * how many we've used so far and how many are pending (i.e., yet ack'ed by T3). 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic unsigned int wrlen __read_mostly; 33462306a36Sopenharmony_cistatic unsigned int skb_wrs[SKB_WR_LIST_SIZE] __read_mostly; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void init_wr_tab(unsigned int wr_len) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci int i; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (skb_wrs[1]) /* already initialized */ 34162306a36Sopenharmony_ci return; 34262306a36Sopenharmony_ci for (i = 1; i < SKB_WR_LIST_SIZE; i++) { 34362306a36Sopenharmony_ci int sgl_len = (3 * i) / 2 + (i & 1); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci sgl_len += 3; 34662306a36Sopenharmony_ci skb_wrs[i] = (sgl_len <= wr_len 34762306a36Sopenharmony_ci ? 1 : 1 + (sgl_len - 2) / (wr_len - 1)); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci wrlen = wr_len * 8; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic inline void make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb, 35362306a36Sopenharmony_ci int len, int req_completion) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct tx_data_wr *req; 35662306a36Sopenharmony_ci struct l2t_entry *l2t = csk->l2t; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci skb_reset_transport_header(skb); 35962306a36Sopenharmony_ci req = __skb_push(skb, sizeof(*req)); 36062306a36Sopenharmony_ci req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA) | 36162306a36Sopenharmony_ci (req_completion ? F_WR_COMPL : 0)); 36262306a36Sopenharmony_ci req->wr_lo = htonl(V_WR_TID(csk->tid)); 36362306a36Sopenharmony_ci /* len includes the length of any HW ULP additions */ 36462306a36Sopenharmony_ci req->len = htonl(len); 36562306a36Sopenharmony_ci /* V_TX_ULP_SUBMODE sets both the mode and submode */ 36662306a36Sopenharmony_ci req->flags = htonl(V_TX_ULP_SUBMODE(cxgbi_skcb_tx_ulp_mode(skb)) | 36762306a36Sopenharmony_ci V_TX_SHOVE((skb_peek(&csk->write_queue) ? 0 : 1))); 36862306a36Sopenharmony_ci req->sndseq = htonl(csk->snd_nxt); 36962306a36Sopenharmony_ci req->param = htonl(V_TX_PORT(l2t->smt_idx)); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT)) { 37262306a36Sopenharmony_ci req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT | 37362306a36Sopenharmony_ci V_TX_CPU_IDX(csk->rss_qid)); 37462306a36Sopenharmony_ci /* sendbuffer is in units of 32KB. */ 37562306a36Sopenharmony_ci req->param |= htonl(V_TX_SNDBUF(csk->snd_win >> 15)); 37662306a36Sopenharmony_ci cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* 38162306a36Sopenharmony_ci * push_tx_frames -- start transmit 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * Prepends TX_DATA_WR or CPL_CLOSE_CON_REQ headers to buffers waiting in a 38462306a36Sopenharmony_ci * connection's send queue and sends them on to T3. Must be called with the 38562306a36Sopenharmony_ci * connection's lock held. Returns the amount of send buffer space that was 38662306a36Sopenharmony_ci * freed as a result of sending queued data to T3. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void arp_failure_skb_discard(struct t3cdev *dev, struct sk_buff *skb) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci kfree_skb(skb); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int push_tx_frames(struct cxgbi_sock *csk, int req_completion) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int total_size = 0; 39762306a36Sopenharmony_ci struct sk_buff *skb; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (unlikely(csk->state < CTP_ESTABLISHED || 40062306a36Sopenharmony_ci csk->state == CTP_CLOSE_WAIT_1 || csk->state >= CTP_ABORTING)) { 40162306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_TX, 40262306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, in closing state.\n", 40362306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid); 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci while (csk->wr_cred && (skb = skb_peek(&csk->write_queue)) != NULL) { 40862306a36Sopenharmony_ci int len = skb->len; /* length before skb_push */ 40962306a36Sopenharmony_ci int frags = skb_shinfo(skb)->nr_frags + (len != skb->data_len); 41062306a36Sopenharmony_ci int wrs_needed = skb_wrs[frags]; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (wrs_needed > 1 && len + sizeof(struct tx_data_wr) <= wrlen) 41362306a36Sopenharmony_ci wrs_needed = 1; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci WARN_ON(frags >= SKB_WR_LIST_SIZE || wrs_needed < 1); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (csk->wr_cred < wrs_needed) { 41862306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_PDU_TX, 41962306a36Sopenharmony_ci "csk 0x%p, skb len %u/%u, frag %u, wr %d<%u.\n", 42062306a36Sopenharmony_ci csk, skb->len, skb->data_len, frags, 42162306a36Sopenharmony_ci wrs_needed, csk->wr_cred); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci __skb_unlink(skb, &csk->write_queue); 42662306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_DATA; 42762306a36Sopenharmony_ci skb->csum = wrs_needed; /* remember this until the WR_ACK */ 42862306a36Sopenharmony_ci csk->wr_cred -= wrs_needed; 42962306a36Sopenharmony_ci csk->wr_una_cred += wrs_needed; 43062306a36Sopenharmony_ci cxgbi_sock_enqueue_wr(csk, skb); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_TX, 43362306a36Sopenharmony_ci "csk 0x%p, enqueue, skb len %u/%u, frag %u, wr %d, " 43462306a36Sopenharmony_ci "left %u, unack %u.\n", 43562306a36Sopenharmony_ci csk, skb->len, skb->data_len, frags, skb->csum, 43662306a36Sopenharmony_ci csk->wr_cred, csk->wr_una_cred); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (likely(cxgbi_skcb_test_flag(skb, SKCBF_TX_NEED_HDR))) { 43962306a36Sopenharmony_ci if ((req_completion && 44062306a36Sopenharmony_ci csk->wr_una_cred == wrs_needed) || 44162306a36Sopenharmony_ci csk->wr_una_cred >= csk->wr_max_cred / 2) { 44262306a36Sopenharmony_ci req_completion = 1; 44362306a36Sopenharmony_ci csk->wr_una_cred = 0; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci len += cxgbi_ulp_extra_len(cxgbi_skcb_tx_ulp_mode(skb)); 44662306a36Sopenharmony_ci make_tx_data_wr(csk, skb, len, req_completion); 44762306a36Sopenharmony_ci csk->snd_nxt += len; 44862306a36Sopenharmony_ci cxgbi_skcb_clear_flag(skb, SKCBF_TX_NEED_HDR); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci total_size += skb->truesize; 45162306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_TX, 45262306a36Sopenharmony_ci "csk 0x%p, tid 0x%x, send skb 0x%p.\n", 45362306a36Sopenharmony_ci csk, csk->tid, skb); 45462306a36Sopenharmony_ci set_arp_failure_handler(skb, arp_failure_skb_discard); 45562306a36Sopenharmony_ci l2t_send(csk->cdev->lldev, skb, csk->l2t); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci return total_size; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/* 46162306a36Sopenharmony_ci * Process a CPL_ACT_ESTABLISH message: -> host 46262306a36Sopenharmony_ci * Updates connection state from an active establish CPL message. Runs with 46362306a36Sopenharmony_ci * the connection lock held. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic inline void free_atid(struct cxgbi_sock *csk) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci if (cxgbi_sock_flag(csk, CTPF_HAS_ATID)) { 46962306a36Sopenharmony_ci cxgb3_free_atid(csk->cdev->lldev, csk->atid); 47062306a36Sopenharmony_ci cxgbi_sock_clear_flag(csk, CTPF_HAS_ATID); 47162306a36Sopenharmony_ci cxgbi_sock_put(csk); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int do_act_establish(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct cxgbi_sock *csk = ctx; 47862306a36Sopenharmony_ci struct cpl_act_establish *req = cplhdr(skb); 47962306a36Sopenharmony_ci unsigned int tid = GET_TID(req); 48062306a36Sopenharmony_ci unsigned int atid = G_PASS_OPEN_TID(ntohl(req->tos_tid)); 48162306a36Sopenharmony_ci u32 rcv_isn = ntohl(req->rcv_isn); /* real RCV_ISN + 1 */ 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 48462306a36Sopenharmony_ci "atid 0x%x,tid 0x%x, csk 0x%p,%u,0x%lx, isn %u.\n", 48562306a36Sopenharmony_ci atid, atid, csk, csk->state, csk->flags, rcv_isn); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci cxgbi_sock_get(csk); 48862306a36Sopenharmony_ci cxgbi_sock_set_flag(csk, CTPF_HAS_TID); 48962306a36Sopenharmony_ci csk->tid = tid; 49062306a36Sopenharmony_ci cxgb3_insert_tid(csk->cdev->lldev, &t3_client, csk, tid); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci free_atid(csk); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci csk->rss_qid = G_QNUM(ntohs(skb->csum)); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci spin_lock_bh(&csk->lock); 49762306a36Sopenharmony_ci if (csk->retry_timer.function) { 49862306a36Sopenharmony_ci del_timer(&csk->retry_timer); 49962306a36Sopenharmony_ci csk->retry_timer.function = NULL; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (unlikely(csk->state != CTP_ACTIVE_OPEN)) 50362306a36Sopenharmony_ci pr_info("csk 0x%p,%u,0x%lx,%u, got EST.\n", 50462306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci csk->copied_seq = csk->rcv_wup = csk->rcv_nxt = rcv_isn; 50762306a36Sopenharmony_ci if (csk->rcv_win > (M_RCV_BUFSIZ << 10)) 50862306a36Sopenharmony_ci csk->rcv_wup -= csk->rcv_win - (M_RCV_BUFSIZ << 10); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt)); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (unlikely(cxgbi_sock_flag(csk, CTPF_ACTIVE_CLOSE_NEEDED))) 51362306a36Sopenharmony_ci /* upper layer has requested closing */ 51462306a36Sopenharmony_ci send_abort_req(csk); 51562306a36Sopenharmony_ci else { 51662306a36Sopenharmony_ci if (skb_queue_len(&csk->write_queue)) 51762306a36Sopenharmony_ci push_tx_frames(csk, 1); 51862306a36Sopenharmony_ci cxgbi_conn_tx_open(csk); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci spin_unlock_bh(&csk->lock); 52262306a36Sopenharmony_ci __kfree_skb(skb); 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* 52762306a36Sopenharmony_ci * Process a CPL_ACT_OPEN_RPL message: -> host 52862306a36Sopenharmony_ci * Handle active open failures. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_cistatic int act_open_rpl_status_to_errno(int status) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci switch (status) { 53362306a36Sopenharmony_ci case CPL_ERR_CONN_RESET: 53462306a36Sopenharmony_ci return -ECONNREFUSED; 53562306a36Sopenharmony_ci case CPL_ERR_ARP_MISS: 53662306a36Sopenharmony_ci return -EHOSTUNREACH; 53762306a36Sopenharmony_ci case CPL_ERR_CONN_TIMEDOUT: 53862306a36Sopenharmony_ci return -ETIMEDOUT; 53962306a36Sopenharmony_ci case CPL_ERR_TCAM_FULL: 54062306a36Sopenharmony_ci return -ENOMEM; 54162306a36Sopenharmony_ci case CPL_ERR_CONN_EXIST: 54262306a36Sopenharmony_ci return -EADDRINUSE; 54362306a36Sopenharmony_ci default: 54462306a36Sopenharmony_ci return -EIO; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void act_open_retry_timer(struct timer_list *t) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct cxgbi_sock *csk = from_timer(csk, t, retry_timer); 55162306a36Sopenharmony_ci struct sk_buff *skb; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 55462306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u.\n", 55562306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci cxgbi_sock_get(csk); 55862306a36Sopenharmony_ci spin_lock_bh(&csk->lock); 55962306a36Sopenharmony_ci skb = alloc_wr(sizeof(struct cpl_act_open_req), 0, GFP_ATOMIC); 56062306a36Sopenharmony_ci if (!skb) 56162306a36Sopenharmony_ci cxgbi_sock_fail_act_open(csk, -ENOMEM); 56262306a36Sopenharmony_ci else { 56362306a36Sopenharmony_ci skb->sk = (struct sock *)csk; 56462306a36Sopenharmony_ci set_arp_failure_handler(skb, act_open_arp_failure); 56562306a36Sopenharmony_ci send_act_open_req(csk, skb, csk->l2t); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci spin_unlock_bh(&csk->lock); 56862306a36Sopenharmony_ci cxgbi_sock_put(csk); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int do_act_open_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct cxgbi_sock *csk = ctx; 57462306a36Sopenharmony_ci struct cpl_act_open_rpl *rpl = cplhdr(skb); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci pr_info("csk 0x%p,%u,0x%lx,%u, status %u, %pI4:%u-%pI4:%u.\n", 57762306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->atid, rpl->status, 57862306a36Sopenharmony_ci &csk->saddr.sin_addr.s_addr, ntohs(csk->saddr.sin_port), 57962306a36Sopenharmony_ci &csk->daddr.sin_addr.s_addr, ntohs(csk->daddr.sin_port)); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (rpl->status != CPL_ERR_TCAM_FULL && 58262306a36Sopenharmony_ci rpl->status != CPL_ERR_CONN_EXIST && 58362306a36Sopenharmony_ci rpl->status != CPL_ERR_ARP_MISS) 58462306a36Sopenharmony_ci cxgb3_queue_tid_release(tdev, GET_TID(rpl)); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci cxgbi_sock_get(csk); 58762306a36Sopenharmony_ci spin_lock_bh(&csk->lock); 58862306a36Sopenharmony_ci if (rpl->status == CPL_ERR_CONN_EXIST && 58962306a36Sopenharmony_ci csk->retry_timer.function != act_open_retry_timer) { 59062306a36Sopenharmony_ci csk->retry_timer.function = act_open_retry_timer; 59162306a36Sopenharmony_ci mod_timer(&csk->retry_timer, jiffies + HZ / 2); 59262306a36Sopenharmony_ci } else 59362306a36Sopenharmony_ci cxgbi_sock_fail_act_open(csk, 59462306a36Sopenharmony_ci act_open_rpl_status_to_errno(rpl->status)); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci spin_unlock_bh(&csk->lock); 59762306a36Sopenharmony_ci cxgbi_sock_put(csk); 59862306a36Sopenharmony_ci __kfree_skb(skb); 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/* 60362306a36Sopenharmony_ci * Process PEER_CLOSE CPL messages: -> host 60462306a36Sopenharmony_ci * Handle peer FIN. 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_cistatic int do_peer_close(struct t3cdev *cdev, struct sk_buff *skb, void *ctx) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct cxgbi_sock *csk = ctx; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 61162306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u.\n", 61262306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci cxgbi_sock_rcv_peer_close(csk); 61562306a36Sopenharmony_ci __kfree_skb(skb); 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci/* 62062306a36Sopenharmony_ci * Process CLOSE_CONN_RPL CPL message: -> host 62162306a36Sopenharmony_ci * Process a peer ACK to our FIN. 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_cistatic int do_close_con_rpl(struct t3cdev *cdev, struct sk_buff *skb, 62462306a36Sopenharmony_ci void *ctx) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct cxgbi_sock *csk = ctx; 62762306a36Sopenharmony_ci struct cpl_close_con_rpl *rpl = cplhdr(skb); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 63062306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, snxt %u.\n", 63162306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid, ntohl(rpl->snd_nxt)); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci cxgbi_sock_rcv_close_conn_rpl(csk, ntohl(rpl->snd_nxt)); 63462306a36Sopenharmony_ci __kfree_skb(skb); 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/* 63962306a36Sopenharmony_ci * Process ABORT_REQ_RSS CPL message: -> host 64062306a36Sopenharmony_ci * Process abort requests. If we are waiting for an ABORT_RPL we ignore this 64162306a36Sopenharmony_ci * request except that we need to reply to it. 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic int abort_status_to_errno(struct cxgbi_sock *csk, int abort_reason, 64562306a36Sopenharmony_ci int *need_rst) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci switch (abort_reason) { 64862306a36Sopenharmony_ci case CPL_ERR_BAD_SYN: 64962306a36Sopenharmony_ci case CPL_ERR_CONN_RESET: 65062306a36Sopenharmony_ci return csk->state > CTP_ESTABLISHED ? -EPIPE : -ECONNRESET; 65162306a36Sopenharmony_ci case CPL_ERR_XMIT_TIMEDOUT: 65262306a36Sopenharmony_ci case CPL_ERR_PERSIST_TIMEDOUT: 65362306a36Sopenharmony_ci case CPL_ERR_FINWAIT2_TIMEDOUT: 65462306a36Sopenharmony_ci case CPL_ERR_KEEPALIVE_TIMEDOUT: 65562306a36Sopenharmony_ci return -ETIMEDOUT; 65662306a36Sopenharmony_ci default: 65762306a36Sopenharmony_ci return -EIO; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int do_abort_req(struct t3cdev *cdev, struct sk_buff *skb, void *ctx) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci const struct cpl_abort_req_rss *req = cplhdr(skb); 66462306a36Sopenharmony_ci struct cxgbi_sock *csk = ctx; 66562306a36Sopenharmony_ci int rst_status = CPL_ABORT_NO_RST; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 66862306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u.\n", 66962306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (req->status == CPL_ERR_RTX_NEG_ADVICE || 67262306a36Sopenharmony_ci req->status == CPL_ERR_PERSIST_NEG_ADVICE) { 67362306a36Sopenharmony_ci goto done; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci cxgbi_sock_get(csk); 67762306a36Sopenharmony_ci spin_lock_bh(&csk->lock); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (!cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD)) { 68062306a36Sopenharmony_ci cxgbi_sock_set_flag(csk, CTPF_ABORT_REQ_RCVD); 68162306a36Sopenharmony_ci cxgbi_sock_set_state(csk, CTP_ABORTING); 68262306a36Sopenharmony_ci goto out; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci cxgbi_sock_clear_flag(csk, CTPF_ABORT_REQ_RCVD); 68662306a36Sopenharmony_ci send_abort_rpl(csk, rst_status); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (!cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) { 68962306a36Sopenharmony_ci csk->err = abort_status_to_errno(csk, req->status, &rst_status); 69062306a36Sopenharmony_ci cxgbi_sock_closed(csk); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ciout: 69462306a36Sopenharmony_ci spin_unlock_bh(&csk->lock); 69562306a36Sopenharmony_ci cxgbi_sock_put(csk); 69662306a36Sopenharmony_cidone: 69762306a36Sopenharmony_ci __kfree_skb(skb); 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci/* 70262306a36Sopenharmony_ci * Process ABORT_RPL_RSS CPL message: -> host 70362306a36Sopenharmony_ci * Process abort replies. We only process these messages if we anticipate 70462306a36Sopenharmony_ci * them as the coordination between SW and HW in this area is somewhat lacking 70562306a36Sopenharmony_ci * and sometimes we get ABORT_RPLs after we are done with the connection that 70662306a36Sopenharmony_ci * originated the ABORT_REQ. 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_cistatic int do_abort_rpl(struct t3cdev *cdev, struct sk_buff *skb, void *ctx) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct cpl_abort_rpl_rss *rpl = cplhdr(skb); 71162306a36Sopenharmony_ci struct cxgbi_sock *csk = ctx; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 71462306a36Sopenharmony_ci "status 0x%x, csk 0x%p, s %u, 0x%lx.\n", 71562306a36Sopenharmony_ci rpl->status, csk, csk ? csk->state : 0, 71662306a36Sopenharmony_ci csk ? csk->flags : 0UL); 71762306a36Sopenharmony_ci /* 71862306a36Sopenharmony_ci * Ignore replies to post-close aborts indicating that the abort was 71962306a36Sopenharmony_ci * requested too late. These connections are terminated when we get 72062306a36Sopenharmony_ci * PEER_CLOSE or CLOSE_CON_RPL and by the time the abort_rpl_rss 72162306a36Sopenharmony_ci * arrives the TID is either no longer used or it has been recycled. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci if (rpl->status == CPL_ERR_ABORT_FAILED) 72462306a36Sopenharmony_ci goto rel_skb; 72562306a36Sopenharmony_ci /* 72662306a36Sopenharmony_ci * Sometimes we've already closed the connection, e.g., a post-close 72762306a36Sopenharmony_ci * abort races with ABORT_REQ_RSS, the latter frees the connection 72862306a36Sopenharmony_ci * expecting the ABORT_REQ will fail with CPL_ERR_ABORT_FAILED, 72962306a36Sopenharmony_ci * but FW turns the ABORT_REQ into a regular one and so we get 73062306a36Sopenharmony_ci * ABORT_RPL_RSS with status 0 and no connection. 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci if (csk) 73362306a36Sopenharmony_ci cxgbi_sock_rcv_abort_rpl(csk); 73462306a36Sopenharmony_cirel_skb: 73562306a36Sopenharmony_ci __kfree_skb(skb); 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/* 74062306a36Sopenharmony_ci * Process RX_ISCSI_HDR CPL message: -> host 74162306a36Sopenharmony_ci * Handle received PDUs, the payload could be DDP'ed. If not, the payload 74262306a36Sopenharmony_ci * follow after the bhs. 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_cistatic int do_iscsi_hdr(struct t3cdev *t3dev, struct sk_buff *skb, void *ctx) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct cxgbi_sock *csk = ctx; 74762306a36Sopenharmony_ci struct cpl_iscsi_hdr *hdr_cpl = cplhdr(skb); 74862306a36Sopenharmony_ci struct cpl_iscsi_hdr_norss data_cpl; 74962306a36Sopenharmony_ci struct cpl_rx_data_ddp_norss ddp_cpl; 75062306a36Sopenharmony_ci unsigned int hdr_len, data_len, status; 75162306a36Sopenharmony_ci unsigned int len; 75262306a36Sopenharmony_ci int err; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX, 75562306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, skb 0x%p,%u.\n", 75662306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid, skb, skb->len); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci spin_lock_bh(&csk->lock); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (unlikely(csk->state >= CTP_PASSIVE_CLOSE)) { 76162306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 76262306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, bad state.\n", 76362306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid); 76462306a36Sopenharmony_ci if (csk->state != CTP_ABORTING) 76562306a36Sopenharmony_ci goto abort_conn; 76662306a36Sopenharmony_ci else 76762306a36Sopenharmony_ci goto discard; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci cxgbi_skcb_tcp_seq(skb) = ntohl(hdr_cpl->seq); 77162306a36Sopenharmony_ci cxgbi_skcb_flags(skb) = 0; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci skb_reset_transport_header(skb); 77462306a36Sopenharmony_ci __skb_pull(skb, sizeof(struct cpl_iscsi_hdr)); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci len = hdr_len = ntohs(hdr_cpl->len); 77762306a36Sopenharmony_ci /* msg coalesce is off or not enough data received */ 77862306a36Sopenharmony_ci if (skb->len <= hdr_len) { 77962306a36Sopenharmony_ci pr_err("%s: tid %u, CPL_ISCSI_HDR, skb len %u < %u.\n", 78062306a36Sopenharmony_ci csk->cdev->ports[csk->port_id]->name, csk->tid, 78162306a36Sopenharmony_ci skb->len, hdr_len); 78262306a36Sopenharmony_ci goto abort_conn; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci cxgbi_skcb_set_flag(skb, SKCBF_RX_COALESCED); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci err = skb_copy_bits(skb, skb->len - sizeof(ddp_cpl), &ddp_cpl, 78762306a36Sopenharmony_ci sizeof(ddp_cpl)); 78862306a36Sopenharmony_ci if (err < 0) { 78962306a36Sopenharmony_ci pr_err("%s: tid %u, copy cpl_ddp %u-%zu failed %d.\n", 79062306a36Sopenharmony_ci csk->cdev->ports[csk->port_id]->name, csk->tid, 79162306a36Sopenharmony_ci skb->len, sizeof(ddp_cpl), err); 79262306a36Sopenharmony_ci goto abort_conn; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci cxgbi_skcb_set_flag(skb, SKCBF_RX_STATUS); 79662306a36Sopenharmony_ci cxgbi_skcb_rx_pdulen(skb) = ntohs(ddp_cpl.len); 79762306a36Sopenharmony_ci cxgbi_skcb_rx_ddigest(skb) = ntohl(ddp_cpl.ulp_crc); 79862306a36Sopenharmony_ci status = ntohl(ddp_cpl.ddp_status); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX, 80162306a36Sopenharmony_ci "csk 0x%p, skb 0x%p,%u, pdulen %u, status 0x%x.\n", 80262306a36Sopenharmony_ci csk, skb, skb->len, cxgbi_skcb_rx_pdulen(skb), status); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (status & (1 << CPL_RX_DDP_STATUS_HCRC_SHIFT)) 80562306a36Sopenharmony_ci cxgbi_skcb_set_flag(skb, SKCBF_RX_HCRC_ERR); 80662306a36Sopenharmony_ci if (status & (1 << CPL_RX_DDP_STATUS_DCRC_SHIFT)) 80762306a36Sopenharmony_ci cxgbi_skcb_set_flag(skb, SKCBF_RX_DCRC_ERR); 80862306a36Sopenharmony_ci if (status & (1 << CPL_RX_DDP_STATUS_PAD_SHIFT)) 80962306a36Sopenharmony_ci cxgbi_skcb_set_flag(skb, SKCBF_RX_PAD_ERR); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (skb->len > (hdr_len + sizeof(ddp_cpl))) { 81262306a36Sopenharmony_ci err = skb_copy_bits(skb, hdr_len, &data_cpl, sizeof(data_cpl)); 81362306a36Sopenharmony_ci if (err < 0) { 81462306a36Sopenharmony_ci pr_err("%s: tid %u, cp %zu/%u failed %d.\n", 81562306a36Sopenharmony_ci csk->cdev->ports[csk->port_id]->name, 81662306a36Sopenharmony_ci csk->tid, sizeof(data_cpl), skb->len, err); 81762306a36Sopenharmony_ci goto abort_conn; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci data_len = ntohs(data_cpl.len); 82062306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_DDP | 1 << CXGBI_DBG_PDU_RX, 82162306a36Sopenharmony_ci "skb 0x%p, pdu not ddp'ed %u/%u, status 0x%x.\n", 82262306a36Sopenharmony_ci skb, data_len, cxgbi_skcb_rx_pdulen(skb), status); 82362306a36Sopenharmony_ci len += sizeof(data_cpl) + data_len; 82462306a36Sopenharmony_ci } else if (status & (1 << CPL_RX_DDP_STATUS_DDP_SHIFT)) 82562306a36Sopenharmony_ci cxgbi_skcb_set_flag(skb, SKCBF_RX_DATA_DDPD); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci csk->rcv_nxt = ntohl(ddp_cpl.seq) + cxgbi_skcb_rx_pdulen(skb); 82862306a36Sopenharmony_ci __pskb_trim(skb, len); 82962306a36Sopenharmony_ci __skb_queue_tail(&csk->receive_queue, skb); 83062306a36Sopenharmony_ci cxgbi_conn_pdu_ready(csk); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci spin_unlock_bh(&csk->lock); 83362306a36Sopenharmony_ci return 0; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ciabort_conn: 83662306a36Sopenharmony_ci send_abort_req(csk); 83762306a36Sopenharmony_cidiscard: 83862306a36Sopenharmony_ci spin_unlock_bh(&csk->lock); 83962306a36Sopenharmony_ci __kfree_skb(skb); 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci/* 84462306a36Sopenharmony_ci * Process TX_DATA_ACK CPL messages: -> host 84562306a36Sopenharmony_ci * Process an acknowledgment of WR completion. Advance snd_una and send the 84662306a36Sopenharmony_ci * next batch of work requests from the write queue. 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_cistatic int do_wr_ack(struct t3cdev *cdev, struct sk_buff *skb, void *ctx) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci struct cxgbi_sock *csk = ctx; 85162306a36Sopenharmony_ci struct cpl_wr_ack *hdr = cplhdr(skb); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX, 85462306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u, cr %u.\n", 85562306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid, ntohs(hdr->credits)); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci cxgbi_sock_rcv_wr_ack(csk, ntohs(hdr->credits), ntohl(hdr->snd_una), 1); 85862306a36Sopenharmony_ci __kfree_skb(skb); 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci/* 86362306a36Sopenharmony_ci * for each connection, pre-allocate skbs needed for close/abort requests. So 86462306a36Sopenharmony_ci * that we can service the request right away. 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_cistatic int alloc_cpls(struct cxgbi_sock *csk) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci csk->cpl_close = alloc_wr(sizeof(struct cpl_close_con_req), 0, 86962306a36Sopenharmony_ci GFP_KERNEL); 87062306a36Sopenharmony_ci if (!csk->cpl_close) 87162306a36Sopenharmony_ci return -ENOMEM; 87262306a36Sopenharmony_ci csk->cpl_abort_req = alloc_wr(sizeof(struct cpl_abort_req), 0, 87362306a36Sopenharmony_ci GFP_KERNEL); 87462306a36Sopenharmony_ci if (!csk->cpl_abort_req) 87562306a36Sopenharmony_ci goto free_cpl_skbs; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci csk->cpl_abort_rpl = alloc_wr(sizeof(struct cpl_abort_rpl), 0, 87862306a36Sopenharmony_ci GFP_KERNEL); 87962306a36Sopenharmony_ci if (!csk->cpl_abort_rpl) 88062306a36Sopenharmony_ci goto free_cpl_skbs; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return 0; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cifree_cpl_skbs: 88562306a36Sopenharmony_ci cxgbi_sock_free_cpl_skbs(csk); 88662306a36Sopenharmony_ci return -ENOMEM; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic void l2t_put(struct cxgbi_sock *csk) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci struct t3cdev *t3dev = (struct t3cdev *)csk->cdev->lldev; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (csk->l2t) { 89462306a36Sopenharmony_ci l2t_release(t3dev, csk->l2t); 89562306a36Sopenharmony_ci csk->l2t = NULL; 89662306a36Sopenharmony_ci cxgbi_sock_put(csk); 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci/* 90162306a36Sopenharmony_ci * release_offload_resources - release offload resource 90262306a36Sopenharmony_ci * Release resources held by an offload connection (TID, L2T entry, etc.) 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_cistatic void release_offload_resources(struct cxgbi_sock *csk) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct t3cdev *t3dev = (struct t3cdev *)csk->cdev->lldev; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 90962306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx,%u.\n", 91062306a36Sopenharmony_ci csk, csk->state, csk->flags, csk->tid); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci csk->rss_qid = 0; 91362306a36Sopenharmony_ci cxgbi_sock_free_cpl_skbs(csk); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (csk->wr_cred != csk->wr_max_cred) { 91662306a36Sopenharmony_ci cxgbi_sock_purge_wr_queue(csk); 91762306a36Sopenharmony_ci cxgbi_sock_reset_wr_list(csk); 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci l2t_put(csk); 92062306a36Sopenharmony_ci if (cxgbi_sock_flag(csk, CTPF_HAS_ATID)) 92162306a36Sopenharmony_ci free_atid(csk); 92262306a36Sopenharmony_ci else if (cxgbi_sock_flag(csk, CTPF_HAS_TID)) { 92362306a36Sopenharmony_ci cxgb3_remove_tid(t3dev, (void *)csk, csk->tid); 92462306a36Sopenharmony_ci cxgbi_sock_clear_flag(csk, CTPF_HAS_TID); 92562306a36Sopenharmony_ci cxgbi_sock_put(csk); 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci csk->dst = NULL; 92862306a36Sopenharmony_ci csk->cdev = NULL; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic void update_address(struct cxgbi_hba *chba) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci if (chba->ipv4addr) { 93462306a36Sopenharmony_ci if (chba->vdev && 93562306a36Sopenharmony_ci chba->ipv4addr != cxgb3i_get_private_ipv4addr(chba->vdev)) { 93662306a36Sopenharmony_ci cxgb3i_set_private_ipv4addr(chba->vdev, chba->ipv4addr); 93762306a36Sopenharmony_ci cxgb3i_set_private_ipv4addr(chba->ndev, 0); 93862306a36Sopenharmony_ci pr_info("%s set %pI4.\n", 93962306a36Sopenharmony_ci chba->vdev->name, &chba->ipv4addr); 94062306a36Sopenharmony_ci } else if (chba->ipv4addr != 94162306a36Sopenharmony_ci cxgb3i_get_private_ipv4addr(chba->ndev)) { 94262306a36Sopenharmony_ci cxgb3i_set_private_ipv4addr(chba->ndev, chba->ipv4addr); 94362306a36Sopenharmony_ci pr_info("%s set %pI4.\n", 94462306a36Sopenharmony_ci chba->ndev->name, &chba->ipv4addr); 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci } else if (cxgb3i_get_private_ipv4addr(chba->ndev)) { 94762306a36Sopenharmony_ci if (chba->vdev) 94862306a36Sopenharmony_ci cxgb3i_set_private_ipv4addr(chba->vdev, 0); 94962306a36Sopenharmony_ci cxgb3i_set_private_ipv4addr(chba->ndev, 0); 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int init_act_open(struct cxgbi_sock *csk) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct dst_entry *dst = csk->dst; 95662306a36Sopenharmony_ci struct cxgbi_device *cdev = csk->cdev; 95762306a36Sopenharmony_ci struct t3cdev *t3dev = (struct t3cdev *)cdev->lldev; 95862306a36Sopenharmony_ci struct net_device *ndev = cdev->ports[csk->port_id]; 95962306a36Sopenharmony_ci struct cxgbi_hba *chba = cdev->hbas[csk->port_id]; 96062306a36Sopenharmony_ci struct sk_buff *skb = NULL; 96162306a36Sopenharmony_ci int ret; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 96462306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx.\n", csk, csk->state, csk->flags); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci update_address(chba); 96762306a36Sopenharmony_ci if (chba->ipv4addr) 96862306a36Sopenharmony_ci csk->saddr.sin_addr.s_addr = chba->ipv4addr; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci csk->rss_qid = 0; 97162306a36Sopenharmony_ci csk->l2t = t3_l2t_get(t3dev, dst, ndev, 97262306a36Sopenharmony_ci &csk->daddr.sin_addr.s_addr); 97362306a36Sopenharmony_ci if (!csk->l2t) { 97462306a36Sopenharmony_ci pr_err("NO l2t available.\n"); 97562306a36Sopenharmony_ci return -EINVAL; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci cxgbi_sock_get(csk); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci csk->atid = cxgb3_alloc_atid(t3dev, &t3_client, csk); 98062306a36Sopenharmony_ci if (csk->atid < 0) { 98162306a36Sopenharmony_ci pr_err("NO atid available.\n"); 98262306a36Sopenharmony_ci ret = -EINVAL; 98362306a36Sopenharmony_ci goto put_sock; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci cxgbi_sock_set_flag(csk, CTPF_HAS_ATID); 98662306a36Sopenharmony_ci cxgbi_sock_get(csk); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci skb = alloc_wr(sizeof(struct cpl_act_open_req), 0, GFP_KERNEL); 98962306a36Sopenharmony_ci if (!skb) { 99062306a36Sopenharmony_ci ret = -ENOMEM; 99162306a36Sopenharmony_ci goto free_atid; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci skb->sk = (struct sock *)csk; 99462306a36Sopenharmony_ci set_arp_failure_handler(skb, act_open_arp_failure); 99562306a36Sopenharmony_ci csk->snd_win = cxgb3i_snd_win; 99662306a36Sopenharmony_ci csk->rcv_win = cxgb3i_rcv_win; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci csk->wr_max_cred = csk->wr_cred = T3C_DATA(t3dev)->max_wrs - 1; 99962306a36Sopenharmony_ci csk->wr_una_cred = 0; 100062306a36Sopenharmony_ci csk->mss_idx = cxgbi_sock_select_mss(csk, dst_mtu(dst)); 100162306a36Sopenharmony_ci cxgbi_sock_reset_wr_list(csk); 100262306a36Sopenharmony_ci csk->err = 0; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, 100562306a36Sopenharmony_ci "csk 0x%p,%u,0x%lx, %pI4:%u-%pI4:%u.\n", 100662306a36Sopenharmony_ci csk, csk->state, csk->flags, 100762306a36Sopenharmony_ci &csk->saddr.sin_addr.s_addr, ntohs(csk->saddr.sin_port), 100862306a36Sopenharmony_ci &csk->daddr.sin_addr.s_addr, ntohs(csk->daddr.sin_port)); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci cxgbi_sock_set_state(csk, CTP_ACTIVE_OPEN); 101162306a36Sopenharmony_ci send_act_open_req(csk, skb, csk->l2t); 101262306a36Sopenharmony_ci return 0; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cifree_atid: 101562306a36Sopenharmony_ci cxgb3_free_atid(t3dev, csk->atid); 101662306a36Sopenharmony_ciput_sock: 101762306a36Sopenharmony_ci cxgbi_sock_put(csk); 101862306a36Sopenharmony_ci l2t_release(t3dev, csk->l2t); 101962306a36Sopenharmony_ci csk->l2t = NULL; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return ret; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cicxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS] = { 102562306a36Sopenharmony_ci [CPL_ACT_ESTABLISH] = do_act_establish, 102662306a36Sopenharmony_ci [CPL_ACT_OPEN_RPL] = do_act_open_rpl, 102762306a36Sopenharmony_ci [CPL_PEER_CLOSE] = do_peer_close, 102862306a36Sopenharmony_ci [CPL_ABORT_REQ_RSS] = do_abort_req, 102962306a36Sopenharmony_ci [CPL_ABORT_RPL_RSS] = do_abort_rpl, 103062306a36Sopenharmony_ci [CPL_CLOSE_CON_RPL] = do_close_con_rpl, 103162306a36Sopenharmony_ci [CPL_TX_DMA_ACK] = do_wr_ack, 103262306a36Sopenharmony_ci [CPL_ISCSI_HDR] = do_iscsi_hdr, 103362306a36Sopenharmony_ci}; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci/** 103662306a36Sopenharmony_ci * cxgb3i_ofld_init - allocate and initialize resources for each adapter found 103762306a36Sopenharmony_ci * @cdev: cxgbi adapter 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_cistatic int cxgb3i_ofld_init(struct cxgbi_device *cdev) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct t3cdev *t3dev = (struct t3cdev *)cdev->lldev; 104262306a36Sopenharmony_ci struct adap_ports port; 104362306a36Sopenharmony_ci struct ofld_page_info rx_page_info; 104462306a36Sopenharmony_ci unsigned int wr_len; 104562306a36Sopenharmony_ci int rc; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (t3dev->ctl(t3dev, GET_WR_LEN, &wr_len) < 0 || 104862306a36Sopenharmony_ci t3dev->ctl(t3dev, GET_PORTS, &port) < 0 || 104962306a36Sopenharmony_ci t3dev->ctl(t3dev, GET_RX_PAGE_INFO, &rx_page_info) < 0) { 105062306a36Sopenharmony_ci pr_warn("t3 0x%p, offload up, ioctl failed.\n", t3dev); 105162306a36Sopenharmony_ci return -EINVAL; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (cxgb3i_max_connect > CXGBI_MAX_CONN) 105562306a36Sopenharmony_ci cxgb3i_max_connect = CXGBI_MAX_CONN; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci rc = cxgbi_device_portmap_create(cdev, cxgb3i_sport_base, 105862306a36Sopenharmony_ci cxgb3i_max_connect); 105962306a36Sopenharmony_ci if (rc < 0) 106062306a36Sopenharmony_ci return rc; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci init_wr_tab(wr_len); 106362306a36Sopenharmony_ci cdev->csk_release_offload_resources = release_offload_resources; 106462306a36Sopenharmony_ci cdev->csk_push_tx_frames = push_tx_frames; 106562306a36Sopenharmony_ci cdev->csk_send_abort_req = send_abort_req; 106662306a36Sopenharmony_ci cdev->csk_send_close_req = send_close_req; 106762306a36Sopenharmony_ci cdev->csk_send_rx_credits = send_rx_credits; 106862306a36Sopenharmony_ci cdev->csk_alloc_cpls = alloc_cpls; 106962306a36Sopenharmony_ci cdev->csk_init_act_open = init_act_open; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci pr_info("cdev 0x%p, offload up, added.\n", cdev); 107262306a36Sopenharmony_ci return 0; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci/* 107662306a36Sopenharmony_ci * functions to program the pagepod in h/w 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_cistatic inline void ulp_mem_io_set_hdr(struct sk_buff *skb, unsigned int addr) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci struct ulp_mem_io *req = (struct ulp_mem_io *)skb->head; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_BYPASS)); 108562306a36Sopenharmony_ci req->cmd_lock_addr = htonl(V_ULP_MEMIO_ADDR(addr >> 5) | 108662306a36Sopenharmony_ci V_ULPTX_CMD(ULP_MEM_WRITE)); 108762306a36Sopenharmony_ci req->len = htonl(V_ULP_MEMIO_DATA_LEN(IPPOD_SIZE >> 5) | 108862306a36Sopenharmony_ci V_ULPTX_NFLITS((IPPOD_SIZE >> 3) + 1)); 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic struct cxgbi_ppm *cdev2ppm(struct cxgbi_device *cdev) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci return ((struct t3cdev *)cdev->lldev)->ulp_iscsi; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic int ddp_set_map(struct cxgbi_ppm *ppm, struct cxgbi_sock *csk, 109762306a36Sopenharmony_ci struct cxgbi_task_tag_info *ttinfo) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci unsigned int idx = ttinfo->idx; 110062306a36Sopenharmony_ci unsigned int npods = ttinfo->npods; 110162306a36Sopenharmony_ci struct scatterlist *sg = ttinfo->sgl; 110262306a36Sopenharmony_ci struct cxgbi_pagepod *ppod; 110362306a36Sopenharmony_ci struct ulp_mem_io *req; 110462306a36Sopenharmony_ci unsigned int sg_off; 110562306a36Sopenharmony_ci unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit; 110662306a36Sopenharmony_ci int i; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci for (i = 0; i < npods; i++, idx++, pm_addr += IPPOD_SIZE) { 110962306a36Sopenharmony_ci struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) + 111062306a36Sopenharmony_ci IPPOD_SIZE, 0, GFP_ATOMIC); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (!skb) 111362306a36Sopenharmony_ci return -ENOMEM; 111462306a36Sopenharmony_ci ulp_mem_io_set_hdr(skb, pm_addr); 111562306a36Sopenharmony_ci req = (struct ulp_mem_io *)skb->head; 111662306a36Sopenharmony_ci ppod = (struct cxgbi_pagepod *)(req + 1); 111762306a36Sopenharmony_ci sg_off = i * PPOD_PAGES_MAX; 111862306a36Sopenharmony_ci cxgbi_ddp_set_one_ppod(ppod, ttinfo, &sg, 111962306a36Sopenharmony_ci &sg_off); 112062306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_CONTROL; 112162306a36Sopenharmony_ci cxgb3_ofld_send(ppm->lldev, skb); 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci return 0; 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_cistatic void ddp_clear_map(struct cxgbi_device *cdev, struct cxgbi_ppm *ppm, 112762306a36Sopenharmony_ci struct cxgbi_task_tag_info *ttinfo) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci unsigned int idx = ttinfo->idx; 113062306a36Sopenharmony_ci unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit; 113162306a36Sopenharmony_ci unsigned int npods = ttinfo->npods; 113262306a36Sopenharmony_ci int i; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_DDP, 113562306a36Sopenharmony_ci "cdev 0x%p, clear idx %u, npods %u.\n", 113662306a36Sopenharmony_ci cdev, idx, npods); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci for (i = 0; i < npods; i++, idx++, pm_addr += IPPOD_SIZE) { 113962306a36Sopenharmony_ci struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) + 114062306a36Sopenharmony_ci IPPOD_SIZE, 0, GFP_ATOMIC); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (!skb) { 114362306a36Sopenharmony_ci pr_err("cdev 0x%p, clear ddp, %u,%d/%u, skb OOM.\n", 114462306a36Sopenharmony_ci cdev, idx, i, npods); 114562306a36Sopenharmony_ci continue; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci ulp_mem_io_set_hdr(skb, pm_addr); 114862306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_CONTROL; 114962306a36Sopenharmony_ci cxgb3_ofld_send(ppm->lldev, skb); 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic int ddp_setup_conn_pgidx(struct cxgbi_sock *csk, 115462306a36Sopenharmony_ci unsigned int tid, int pg_idx) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci struct sk_buff *skb = alloc_wr(sizeof(struct cpl_set_tcb_field), 0, 115762306a36Sopenharmony_ci GFP_KERNEL); 115862306a36Sopenharmony_ci struct cpl_set_tcb_field *req; 115962306a36Sopenharmony_ci u64 val = pg_idx < DDP_PGIDX_MAX ? pg_idx : 0; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_DDP, 116262306a36Sopenharmony_ci "csk 0x%p, tid %u, pg_idx %d.\n", csk, tid, pg_idx); 116362306a36Sopenharmony_ci if (!skb) 116462306a36Sopenharmony_ci return -ENOMEM; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* set up ulp submode and page size */ 116762306a36Sopenharmony_ci req = (struct cpl_set_tcb_field *)skb->head; 116862306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 116962306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid)); 117062306a36Sopenharmony_ci req->reply = V_NO_REPLY(1); 117162306a36Sopenharmony_ci req->cpu_idx = 0; 117262306a36Sopenharmony_ci req->word = htons(31); 117362306a36Sopenharmony_ci req->mask = cpu_to_be64(0xF0000000); 117462306a36Sopenharmony_ci req->val = cpu_to_be64(val << 28); 117562306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_CONTROL; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci cxgb3_ofld_send(csk->cdev->lldev, skb); 117862306a36Sopenharmony_ci return 0; 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci/** 118262306a36Sopenharmony_ci * ddp_setup_conn_digest - setup conn. digest setting 118362306a36Sopenharmony_ci * @csk: cxgb tcp socket 118462306a36Sopenharmony_ci * @tid: connection id 118562306a36Sopenharmony_ci * @hcrc: header digest enabled 118662306a36Sopenharmony_ci * @dcrc: data digest enabled 118762306a36Sopenharmony_ci * set up the iscsi digest settings for a connection identified by tid 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_cistatic int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid, 119062306a36Sopenharmony_ci int hcrc, int dcrc) 119162306a36Sopenharmony_ci{ 119262306a36Sopenharmony_ci struct sk_buff *skb = alloc_wr(sizeof(struct cpl_set_tcb_field), 0, 119362306a36Sopenharmony_ci GFP_KERNEL); 119462306a36Sopenharmony_ci struct cpl_set_tcb_field *req; 119562306a36Sopenharmony_ci u64 val = (hcrc ? 1 : 0) | (dcrc ? 2 : 0); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_DDP, 119862306a36Sopenharmony_ci "csk 0x%p, tid %u, crc %d,%d.\n", csk, tid, hcrc, dcrc); 119962306a36Sopenharmony_ci if (!skb) 120062306a36Sopenharmony_ci return -ENOMEM; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* set up ulp submode and page size */ 120362306a36Sopenharmony_ci req = (struct cpl_set_tcb_field *)skb->head; 120462306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 120562306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid)); 120662306a36Sopenharmony_ci req->reply = V_NO_REPLY(1); 120762306a36Sopenharmony_ci req->cpu_idx = 0; 120862306a36Sopenharmony_ci req->word = htons(31); 120962306a36Sopenharmony_ci req->mask = cpu_to_be64(0x0F000000); 121062306a36Sopenharmony_ci req->val = cpu_to_be64(val << 24); 121162306a36Sopenharmony_ci skb->priority = CPL_PRIORITY_CONTROL; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci cxgb3_ofld_send(csk->cdev->lldev, skb); 121462306a36Sopenharmony_ci return 0; 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci/** 121862306a36Sopenharmony_ci * cxgb3i_ddp_init - initialize the cxgb3 adapter's ddp resource 121962306a36Sopenharmony_ci * @cdev: cxgb3i adapter 122062306a36Sopenharmony_ci * initialize the ddp pagepod manager for a given adapter 122162306a36Sopenharmony_ci */ 122262306a36Sopenharmony_cistatic int cxgb3i_ddp_init(struct cxgbi_device *cdev) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci struct t3cdev *tdev = (struct t3cdev *)cdev->lldev; 122562306a36Sopenharmony_ci struct net_device *ndev = cdev->ports[0]; 122662306a36Sopenharmony_ci struct cxgbi_tag_format tformat; 122762306a36Sopenharmony_ci unsigned int ppmax, tagmask = 0; 122862306a36Sopenharmony_ci struct ulp_iscsi_info uinfo; 122962306a36Sopenharmony_ci int i, err; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci err = tdev->ctl(tdev, ULP_ISCSI_GET_PARAMS, &uinfo); 123262306a36Sopenharmony_ci if (err < 0) { 123362306a36Sopenharmony_ci pr_err("%s, failed to get iscsi param %d.\n", 123462306a36Sopenharmony_ci ndev->name, err); 123562306a36Sopenharmony_ci return err; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci if (uinfo.llimit >= uinfo.ulimit) { 123862306a36Sopenharmony_ci pr_warn("T3 %s, iscsi NOT enabled %u ~ %u!\n", 123962306a36Sopenharmony_ci ndev->name, uinfo.llimit, uinfo.ulimit); 124062306a36Sopenharmony_ci return -EACCES; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci ppmax = (uinfo.ulimit - uinfo.llimit + 1) >> PPOD_SIZE_SHIFT; 124462306a36Sopenharmony_ci tagmask = cxgbi_tagmask_set(ppmax); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci pr_info("T3 %s: 0x%x~0x%x, 0x%x, tagmask 0x%x -> 0x%x.\n", 124762306a36Sopenharmony_ci ndev->name, uinfo.llimit, uinfo.ulimit, ppmax, uinfo.tagmask, 124862306a36Sopenharmony_ci tagmask); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci memset(&tformat, 0, sizeof(struct cxgbi_tag_format)); 125162306a36Sopenharmony_ci for (i = 0; i < 4; i++) 125262306a36Sopenharmony_ci tformat.pgsz_order[i] = uinfo.pgsz_factor[i]; 125362306a36Sopenharmony_ci cxgbi_tagmask_check(tagmask, &tformat); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci err = cxgbi_ddp_ppm_setup(&tdev->ulp_iscsi, cdev, &tformat, 125662306a36Sopenharmony_ci (uinfo.ulimit - uinfo.llimit + 1), 125762306a36Sopenharmony_ci uinfo.llimit, uinfo.llimit, 0, 0, 0); 125862306a36Sopenharmony_ci if (err) 125962306a36Sopenharmony_ci return err; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci if (!(cdev->flags & CXGBI_FLAG_DDP_OFF)) { 126262306a36Sopenharmony_ci uinfo.tagmask = tagmask; 126362306a36Sopenharmony_ci uinfo.ulimit = uinfo.llimit + (ppmax << PPOD_SIZE_SHIFT); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci err = tdev->ctl(tdev, ULP_ISCSI_SET_PARAMS, &uinfo); 126662306a36Sopenharmony_ci if (err < 0) { 126762306a36Sopenharmony_ci pr_err("T3 %s fail to set iscsi param %d.\n", 126862306a36Sopenharmony_ci ndev->name, err); 126962306a36Sopenharmony_ci cdev->flags |= CXGBI_FLAG_DDP_OFF; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci err = 0; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci cdev->csk_ddp_setup_digest = ddp_setup_conn_digest; 127562306a36Sopenharmony_ci cdev->csk_ddp_setup_pgidx = ddp_setup_conn_pgidx; 127662306a36Sopenharmony_ci cdev->csk_ddp_set_map = ddp_set_map; 127762306a36Sopenharmony_ci cdev->csk_ddp_clear_map = ddp_clear_map; 127862306a36Sopenharmony_ci cdev->cdev2ppm = cdev2ppm; 127962306a36Sopenharmony_ci cdev->tx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD, 128062306a36Sopenharmony_ci uinfo.max_txsz - ISCSI_PDU_NONPAYLOAD_LEN); 128162306a36Sopenharmony_ci cdev->rx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD, 128262306a36Sopenharmony_ci uinfo.max_rxsz - ISCSI_PDU_NONPAYLOAD_LEN); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci return 0; 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic void cxgb3i_dev_close(struct t3cdev *t3dev) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci struct cxgbi_device *cdev = cxgbi_device_find_by_lldev(t3dev); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (!cdev || cdev->flags & CXGBI_FLAG_ADAPTER_RESET) { 129262306a36Sopenharmony_ci pr_info("0x%p close, f 0x%x.\n", cdev, cdev ? cdev->flags : 0); 129362306a36Sopenharmony_ci return; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci cxgbi_device_unregister(cdev); 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci/** 130062306a36Sopenharmony_ci * cxgb3i_dev_open - init a t3 adapter structure and any h/w settings 130162306a36Sopenharmony_ci * @t3dev: t3cdev adapter 130262306a36Sopenharmony_ci */ 130362306a36Sopenharmony_cistatic void cxgb3i_dev_open(struct t3cdev *t3dev) 130462306a36Sopenharmony_ci{ 130562306a36Sopenharmony_ci struct cxgbi_device *cdev = cxgbi_device_find_by_lldev(t3dev); 130662306a36Sopenharmony_ci struct adapter *adapter = tdev2adap(t3dev); 130762306a36Sopenharmony_ci int i, err; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (cdev) { 131062306a36Sopenharmony_ci pr_info("0x%p, updating.\n", cdev); 131162306a36Sopenharmony_ci return; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci cdev = cxgbi_device_register(0, adapter->params.nports); 131562306a36Sopenharmony_ci if (!cdev) { 131662306a36Sopenharmony_ci pr_warn("device 0x%p register failed.\n", t3dev); 131762306a36Sopenharmony_ci return; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci cdev->flags = CXGBI_FLAG_DEV_T3 | CXGBI_FLAG_IPV4_SET; 132162306a36Sopenharmony_ci cdev->lldev = t3dev; 132262306a36Sopenharmony_ci cdev->pdev = adapter->pdev; 132362306a36Sopenharmony_ci cdev->ports = adapter->port; 132462306a36Sopenharmony_ci cdev->nports = adapter->params.nports; 132562306a36Sopenharmony_ci cdev->mtus = adapter->params.mtus; 132662306a36Sopenharmony_ci cdev->nmtus = NMTUS; 132762306a36Sopenharmony_ci cdev->rx_credit_thres = cxgb3i_rx_credit_thres; 132862306a36Sopenharmony_ci cdev->skb_tx_rsvd = CXGB3I_TX_HEADER_LEN; 132962306a36Sopenharmony_ci cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr_norss); 133062306a36Sopenharmony_ci cdev->itp = &cxgb3i_iscsi_transport; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci err = cxgb3i_ddp_init(cdev); 133362306a36Sopenharmony_ci if (err) { 133462306a36Sopenharmony_ci pr_info("0x%p ddp init failed %d\n", cdev, err); 133562306a36Sopenharmony_ci goto err_out; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci err = cxgb3i_ofld_init(cdev); 133962306a36Sopenharmony_ci if (err) { 134062306a36Sopenharmony_ci pr_info("0x%p offload init failed\n", cdev); 134162306a36Sopenharmony_ci goto err_out; 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci err = cxgbi_hbas_add(cdev, CXGB3I_MAX_LUN, CXGBI_MAX_CONN, 134562306a36Sopenharmony_ci &cxgb3i_host_template, cxgb3i_stt); 134662306a36Sopenharmony_ci if (err) 134762306a36Sopenharmony_ci goto err_out; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci for (i = 0; i < cdev->nports; i++) 135062306a36Sopenharmony_ci cdev->hbas[i]->ipv4addr = 135162306a36Sopenharmony_ci cxgb3i_get_private_ipv4addr(cdev->ports[i]); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci pr_info("cdev 0x%p, f 0x%x, t3dev 0x%p open, err %d.\n", 135462306a36Sopenharmony_ci cdev, cdev ? cdev->flags : 0, t3dev, err); 135562306a36Sopenharmony_ci return; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_cierr_out: 135862306a36Sopenharmony_ci cxgbi_device_unregister(cdev); 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_cistatic void cxgb3i_dev_event_handler(struct t3cdev *t3dev, u32 event, u32 port) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci struct cxgbi_device *cdev = cxgbi_device_find_by_lldev(t3dev); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci log_debug(1 << CXGBI_DBG_TOE, 136662306a36Sopenharmony_ci "0x%p, cdev 0x%p, event 0x%x, port 0x%x.\n", 136762306a36Sopenharmony_ci t3dev, cdev, event, port); 136862306a36Sopenharmony_ci if (!cdev) 136962306a36Sopenharmony_ci return; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci switch (event) { 137262306a36Sopenharmony_ci case OFFLOAD_STATUS_DOWN: 137362306a36Sopenharmony_ci cdev->flags |= CXGBI_FLAG_ADAPTER_RESET; 137462306a36Sopenharmony_ci break; 137562306a36Sopenharmony_ci case OFFLOAD_STATUS_UP: 137662306a36Sopenharmony_ci cdev->flags &= ~CXGBI_FLAG_ADAPTER_RESET; 137762306a36Sopenharmony_ci break; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci/** 138262306a36Sopenharmony_ci * cxgb3i_init_module - module init entry point 138362306a36Sopenharmony_ci * 138462306a36Sopenharmony_ci * initialize any driver wide global data structures and register itself 138562306a36Sopenharmony_ci * with the cxgb3 module 138662306a36Sopenharmony_ci */ 138762306a36Sopenharmony_cistatic int __init cxgb3i_init_module(void) 138862306a36Sopenharmony_ci{ 138962306a36Sopenharmony_ci int rc; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci printk(KERN_INFO "%s", version); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci rc = cxgbi_iscsi_init(&cxgb3i_iscsi_transport, &cxgb3i_stt); 139462306a36Sopenharmony_ci if (rc < 0) 139562306a36Sopenharmony_ci return rc; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci cxgb3_register_client(&t3_client); 139862306a36Sopenharmony_ci return 0; 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci/** 140262306a36Sopenharmony_ci * cxgb3i_exit_module - module cleanup/exit entry point 140362306a36Sopenharmony_ci * 140462306a36Sopenharmony_ci * go through the driver hba list and for each hba, release any resource held. 140562306a36Sopenharmony_ci * and unregisters iscsi transport and the cxgb3 module 140662306a36Sopenharmony_ci */ 140762306a36Sopenharmony_cistatic void __exit cxgb3i_exit_module(void) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci cxgb3_unregister_client(&t3_client); 141062306a36Sopenharmony_ci cxgbi_device_unregister_all(CXGBI_FLAG_DEV_T3); 141162306a36Sopenharmony_ci cxgbi_iscsi_cleanup(&cxgb3i_iscsi_transport, &cxgb3i_stt); 141262306a36Sopenharmony_ci} 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_cimodule_init(cxgb3i_init_module); 141562306a36Sopenharmony_cimodule_exit(cxgb3i_exit_module); 1416