18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * cxgb3i_offload.c: Chelsio S3xx iscsi offloaded tcp connection management
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2003-2015 Chelsio Communications.  All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but WITHOUT
78c2ecf20Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
88c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
98c2ecf20Sopenharmony_ci * release for licensing terms and conditions.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Written by:	Dimitris Michailidis (dm@chelsio.com)
128c2ecf20Sopenharmony_ci *		Karen Xie (kxie@chelsio.com)
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
198c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "common.h"
228c2ecf20Sopenharmony_ci#include "t3_cpl.h"
238c2ecf20Sopenharmony_ci#include "t3cdev.h"
248c2ecf20Sopenharmony_ci#include "cxgb3_defs.h"
258c2ecf20Sopenharmony_ci#include "cxgb3_ctl_defs.h"
268c2ecf20Sopenharmony_ci#include "cxgb3_offload.h"
278c2ecf20Sopenharmony_ci#include "firmware_exports.h"
288c2ecf20Sopenharmony_ci#include "cxgb3i.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic unsigned int dbg_level;
318c2ecf20Sopenharmony_ci#include "../libcxgbi.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define DRV_MODULE_NAME         "cxgb3i"
348c2ecf20Sopenharmony_ci#define DRV_MODULE_DESC         "Chelsio T3 iSCSI Driver"
358c2ecf20Sopenharmony_ci#define DRV_MODULE_VERSION	"2.0.1-ko"
368c2ecf20Sopenharmony_ci#define DRV_MODULE_RELDATE	"Apr. 2015"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic char version[] =
398c2ecf20Sopenharmony_ci	DRV_MODULE_DESC " " DRV_MODULE_NAME
408c2ecf20Sopenharmony_ci	" v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications, Inc.");
438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_MODULE_DESC);
448c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION);
458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cimodule_param(dbg_level, uint, 0644);
488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dbg_level, "debug flag (default=0)");
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int cxgb3i_rcv_win = 256 * 1024;
518c2ecf20Sopenharmony_cimodule_param(cxgb3i_rcv_win, int, 0644);
528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_rcv_win, "TCP receive window in bytes (default=256KB)");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int cxgb3i_snd_win = 128 * 1024;
558c2ecf20Sopenharmony_cimodule_param(cxgb3i_snd_win, int, 0644);
568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_snd_win, "TCP send window in bytes (default=128KB)");
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int cxgb3i_rx_credit_thres = 10 * 1024;
598c2ecf20Sopenharmony_cimodule_param(cxgb3i_rx_credit_thres, int, 0644);
608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_rx_credit_thres,
618c2ecf20Sopenharmony_ci		 "RX credits return threshold in bytes (default=10KB)");
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic unsigned int cxgb3i_max_connect = 8 * 1024;
648c2ecf20Sopenharmony_cimodule_param(cxgb3i_max_connect, uint, 0644);
658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_max_connect, "Max. # of connections (default=8092)");
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic unsigned int cxgb3i_sport_base = 20000;
688c2ecf20Sopenharmony_cimodule_param(cxgb3i_sport_base, uint, 0644);
698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cxgb3i_sport_base, "starting port number (default=20000)");
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void cxgb3i_dev_open(struct t3cdev *);
728c2ecf20Sopenharmony_cistatic void cxgb3i_dev_close(struct t3cdev *);
738c2ecf20Sopenharmony_cistatic void cxgb3i_dev_event_handler(struct t3cdev *, u32, u32);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic struct cxgb3_client t3_client = {
768c2ecf20Sopenharmony_ci	.name = DRV_MODULE_NAME,
778c2ecf20Sopenharmony_ci	.handlers = cxgb3i_cpl_handlers,
788c2ecf20Sopenharmony_ci	.add = cxgb3i_dev_open,
798c2ecf20Sopenharmony_ci	.remove = cxgb3i_dev_close,
808c2ecf20Sopenharmony_ci	.event_handler = cxgb3i_dev_event_handler,
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic struct scsi_host_template cxgb3i_host_template = {
848c2ecf20Sopenharmony_ci	.module		= THIS_MODULE,
858c2ecf20Sopenharmony_ci	.name		= DRV_MODULE_NAME,
868c2ecf20Sopenharmony_ci	.proc_name	= DRV_MODULE_NAME,
878c2ecf20Sopenharmony_ci	.can_queue	= CXGB3I_SCSI_HOST_QDEPTH,
888c2ecf20Sopenharmony_ci	.queuecommand	= iscsi_queuecommand,
898c2ecf20Sopenharmony_ci	.change_queue_depth = scsi_change_queue_depth,
908c2ecf20Sopenharmony_ci	.sg_tablesize	= SG_ALL,
918c2ecf20Sopenharmony_ci	.max_sectors	= 0xFFFF,
928c2ecf20Sopenharmony_ci	.cmd_per_lun	= ISCSI_DEF_CMD_PER_LUN,
938c2ecf20Sopenharmony_ci	.eh_timed_out	= iscsi_eh_cmd_timed_out,
948c2ecf20Sopenharmony_ci	.eh_abort_handler = iscsi_eh_abort,
958c2ecf20Sopenharmony_ci	.eh_device_reset_handler = iscsi_eh_device_reset,
968c2ecf20Sopenharmony_ci	.eh_target_reset_handler = iscsi_eh_recover_target,
978c2ecf20Sopenharmony_ci	.target_alloc	= iscsi_target_alloc,
988c2ecf20Sopenharmony_ci	.dma_boundary	= PAGE_SIZE - 1,
998c2ecf20Sopenharmony_ci	.this_id	= -1,
1008c2ecf20Sopenharmony_ci	.track_queue_depth = 1,
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic struct iscsi_transport cxgb3i_iscsi_transport = {
1048c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1058c2ecf20Sopenharmony_ci	.name		= DRV_MODULE_NAME,
1068c2ecf20Sopenharmony_ci	/* owner and name should be set already */
1078c2ecf20Sopenharmony_ci	.caps		= CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
1088c2ecf20Sopenharmony_ci				| CAP_DATADGST | CAP_DIGEST_OFFLOAD |
1098c2ecf20Sopenharmony_ci				CAP_PADDING_OFFLOAD | CAP_TEXT_NEGO,
1108c2ecf20Sopenharmony_ci	.attr_is_visible	= cxgbi_attr_is_visible,
1118c2ecf20Sopenharmony_ci	.get_host_param	= cxgbi_get_host_param,
1128c2ecf20Sopenharmony_ci	.set_host_param	= cxgbi_set_host_param,
1138c2ecf20Sopenharmony_ci	/* session management */
1148c2ecf20Sopenharmony_ci	.create_session	= cxgbi_create_session,
1158c2ecf20Sopenharmony_ci	.destroy_session	= cxgbi_destroy_session,
1168c2ecf20Sopenharmony_ci	.get_session_param = iscsi_session_get_param,
1178c2ecf20Sopenharmony_ci	/* connection management */
1188c2ecf20Sopenharmony_ci	.create_conn	= cxgbi_create_conn,
1198c2ecf20Sopenharmony_ci	.bind_conn	= cxgbi_bind_conn,
1208c2ecf20Sopenharmony_ci	.unbind_conn	= iscsi_conn_unbind,
1218c2ecf20Sopenharmony_ci	.destroy_conn	= iscsi_tcp_conn_teardown,
1228c2ecf20Sopenharmony_ci	.start_conn	= iscsi_conn_start,
1238c2ecf20Sopenharmony_ci	.stop_conn	= iscsi_conn_stop,
1248c2ecf20Sopenharmony_ci	.get_conn_param	= iscsi_conn_get_param,
1258c2ecf20Sopenharmony_ci	.set_param	= cxgbi_set_conn_param,
1268c2ecf20Sopenharmony_ci	.get_stats	= cxgbi_get_conn_stats,
1278c2ecf20Sopenharmony_ci	/* pdu xmit req from user space */
1288c2ecf20Sopenharmony_ci	.send_pdu	= iscsi_conn_send_pdu,
1298c2ecf20Sopenharmony_ci	/* task */
1308c2ecf20Sopenharmony_ci	.init_task	= iscsi_tcp_task_init,
1318c2ecf20Sopenharmony_ci	.xmit_task	= iscsi_tcp_task_xmit,
1328c2ecf20Sopenharmony_ci	.cleanup_task	= cxgbi_cleanup_task,
1338c2ecf20Sopenharmony_ci	/* pdu */
1348c2ecf20Sopenharmony_ci	.alloc_pdu	= cxgbi_conn_alloc_pdu,
1358c2ecf20Sopenharmony_ci	.init_pdu	= cxgbi_conn_init_pdu,
1368c2ecf20Sopenharmony_ci	.xmit_pdu	= cxgbi_conn_xmit_pdu,
1378c2ecf20Sopenharmony_ci	.parse_pdu_itt	= cxgbi_parse_pdu_itt,
1388c2ecf20Sopenharmony_ci	/* TCP connect/disconnect */
1398c2ecf20Sopenharmony_ci	.get_ep_param	= cxgbi_get_ep_param,
1408c2ecf20Sopenharmony_ci	.ep_connect	= cxgbi_ep_connect,
1418c2ecf20Sopenharmony_ci	.ep_poll	= cxgbi_ep_poll,
1428c2ecf20Sopenharmony_ci	.ep_disconnect	= cxgbi_ep_disconnect,
1438c2ecf20Sopenharmony_ci	/* Error recovery timeout call */
1448c2ecf20Sopenharmony_ci	.session_recovery_timedout = iscsi_session_recovery_timedout,
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic struct scsi_transport_template *cxgb3i_stt;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * CPL (Chelsio Protocol Language) defines a message passing interface between
1518c2ecf20Sopenharmony_ci * the host driver and Chelsio asic.
1528c2ecf20Sopenharmony_ci * The section below implments CPLs that related to iscsi tcp connection
1538c2ecf20Sopenharmony_ci * open/close/abort and data send/receive.
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int push_tx_frames(struct cxgbi_sock *csk, int req_completion);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
1598c2ecf20Sopenharmony_ci			      const struct l2t_entry *e)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	unsigned int wscale = cxgbi_sock_compute_wscale(csk->rcv_win);
1628c2ecf20Sopenharmony_ci	struct cpl_act_open_req *req = (struct cpl_act_open_req *)skb->head;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	skb->priority = CPL_PRIORITY_SETUP;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
1678c2ecf20Sopenharmony_ci	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ACT_OPEN_REQ, csk->atid));
1688c2ecf20Sopenharmony_ci	req->local_port = csk->saddr.sin_port;
1698c2ecf20Sopenharmony_ci	req->peer_port = csk->daddr.sin_port;
1708c2ecf20Sopenharmony_ci	req->local_ip = csk->saddr.sin_addr.s_addr;
1718c2ecf20Sopenharmony_ci	req->peer_ip = csk->daddr.sin_addr.s_addr;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	req->opt0h = htonl(V_KEEP_ALIVE(1) | F_TCAM_BYPASS |
1748c2ecf20Sopenharmony_ci			V_WND_SCALE(wscale) | V_MSS_IDX(csk->mss_idx) |
1758c2ecf20Sopenharmony_ci			V_L2T_IDX(e->idx) | V_TX_CHANNEL(e->smt_idx));
1768c2ecf20Sopenharmony_ci	req->opt0l = htonl(V_ULP_MODE(ULP2_MODE_ISCSI) |
1778c2ecf20Sopenharmony_ci			V_RCV_BUFSIZ(csk->rcv_win >> 10));
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
1808c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u, %pI4:%u-%pI4:%u, %u,%u,%u.\n",
1818c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->atid,
1828c2ecf20Sopenharmony_ci		&req->local_ip, ntohs(req->local_port),
1838c2ecf20Sopenharmony_ci		&req->peer_ip, ntohs(req->peer_port),
1848c2ecf20Sopenharmony_ci		csk->mss_idx, e->idx, e->smt_idx);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	l2t_send(csk->cdev->lldev, skb, csk->l2t);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic inline void act_open_arp_failure(struct t3cdev *dev, struct sk_buff *skb)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	cxgbi_sock_act_open_req_arp_failure(NULL, skb);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/*
1958c2ecf20Sopenharmony_ci * CPL connection close request: host ->
1968c2ecf20Sopenharmony_ci *
1978c2ecf20Sopenharmony_ci * Close a connection by sending a CPL_CLOSE_CON_REQ message and queue it to
1988c2ecf20Sopenharmony_ci * the write queue (i.e., after any unsent txt data).
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_cistatic void send_close_req(struct cxgbi_sock *csk)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct sk_buff *skb = csk->cpl_close;
2038c2ecf20Sopenharmony_ci	struct cpl_close_con_req *req = (struct cpl_close_con_req *)skb->head;
2048c2ecf20Sopenharmony_ci	unsigned int tid = csk->tid;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
2078c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u.\n",
2088c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	csk->cpl_close = NULL;
2118c2ecf20Sopenharmony_ci	req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_CLOSE_CON));
2128c2ecf20Sopenharmony_ci	req->wr.wr_lo = htonl(V_WR_TID(tid));
2138c2ecf20Sopenharmony_ci	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_CON_REQ, tid));
2148c2ecf20Sopenharmony_ci	req->rsvd = htonl(csk->write_seq);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	cxgbi_sock_skb_entail(csk, skb);
2178c2ecf20Sopenharmony_ci	if (csk->state >= CTP_ESTABLISHED)
2188c2ecf20Sopenharmony_ci		push_tx_frames(csk, 1);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/*
2228c2ecf20Sopenharmony_ci * CPL connection abort request: host ->
2238c2ecf20Sopenharmony_ci *
2248c2ecf20Sopenharmony_ci * Send an ABORT_REQ message. Makes sure we do not send multiple ABORT_REQs
2258c2ecf20Sopenharmony_ci * for the same connection and also that we do not try to send a message
2268c2ecf20Sopenharmony_ci * after the connection has closed.
2278c2ecf20Sopenharmony_ci */
2288c2ecf20Sopenharmony_cistatic void abort_arp_failure(struct t3cdev *tdev, struct sk_buff *skb)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct cpl_abort_req *req = cplhdr(skb);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
2338c2ecf20Sopenharmony_ci		"t3dev 0x%p, tid %u, skb 0x%p.\n",
2348c2ecf20Sopenharmony_ci		tdev, GET_TID(req), skb);
2358c2ecf20Sopenharmony_ci	req->cmd = CPL_ABORT_NO_RST;
2368c2ecf20Sopenharmony_ci	cxgb3_ofld_send(tdev, skb);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic void send_abort_req(struct cxgbi_sock *csk)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct sk_buff *skb = csk->cpl_abort_req;
2428c2ecf20Sopenharmony_ci	struct cpl_abort_req *req;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (unlikely(csk->state == CTP_ABORTING || !skb))
2458c2ecf20Sopenharmony_ci		return;
2468c2ecf20Sopenharmony_ci	cxgbi_sock_set_state(csk, CTP_ABORTING);
2478c2ecf20Sopenharmony_ci	cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_PENDING);
2488c2ecf20Sopenharmony_ci	/* Purge the send queue so we don't send anything after an abort. */
2498c2ecf20Sopenharmony_ci	cxgbi_sock_purge_write_queue(csk);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	csk->cpl_abort_req = NULL;
2528c2ecf20Sopenharmony_ci	req = (struct cpl_abort_req *)skb->head;
2538c2ecf20Sopenharmony_ci	skb->priority = CPL_PRIORITY_DATA;
2548c2ecf20Sopenharmony_ci	set_arp_failure_handler(skb, abort_arp_failure);
2558c2ecf20Sopenharmony_ci	req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_REQ));
2568c2ecf20Sopenharmony_ci	req->wr.wr_lo = htonl(V_WR_TID(csk->tid));
2578c2ecf20Sopenharmony_ci	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ABORT_REQ, csk->tid));
2588c2ecf20Sopenharmony_ci	req->rsvd0 = htonl(csk->snd_nxt);
2598c2ecf20Sopenharmony_ci	req->rsvd1 = !cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT);
2608c2ecf20Sopenharmony_ci	req->cmd = CPL_ABORT_SEND_RST;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
2638c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u, snd_nxt %u, 0x%x.\n",
2648c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid, csk->snd_nxt,
2658c2ecf20Sopenharmony_ci		req->rsvd1);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	l2t_send(csk->cdev->lldev, skb, csk->l2t);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/*
2718c2ecf20Sopenharmony_ci * CPL connection abort reply: host ->
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * Send an ABORT_RPL message in response of the ABORT_REQ received.
2748c2ecf20Sopenharmony_ci */
2758c2ecf20Sopenharmony_cistatic void send_abort_rpl(struct cxgbi_sock *csk, int rst_status)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct sk_buff *skb = csk->cpl_abort_rpl;
2788c2ecf20Sopenharmony_ci	struct cpl_abort_rpl *rpl = (struct cpl_abort_rpl *)skb->head;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
2818c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u, status %d.\n",
2828c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid, rst_status);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	csk->cpl_abort_rpl = NULL;
2858c2ecf20Sopenharmony_ci	skb->priority = CPL_PRIORITY_DATA;
2868c2ecf20Sopenharmony_ci	rpl->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL));
2878c2ecf20Sopenharmony_ci	rpl->wr.wr_lo = htonl(V_WR_TID(csk->tid));
2888c2ecf20Sopenharmony_ci	OPCODE_TID(rpl) = htonl(MK_OPCODE_TID(CPL_ABORT_RPL, csk->tid));
2898c2ecf20Sopenharmony_ci	rpl->cmd = rst_status;
2908c2ecf20Sopenharmony_ci	cxgb3_ofld_send(csk->cdev->lldev, skb);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/*
2948c2ecf20Sopenharmony_ci * CPL connection rx data ack: host ->
2958c2ecf20Sopenharmony_ci * Send RX credits through an RX_DATA_ACK CPL message. Returns the number of
2968c2ecf20Sopenharmony_ci * credits sent.
2978c2ecf20Sopenharmony_ci */
2988c2ecf20Sopenharmony_cistatic u32 send_rx_credits(struct cxgbi_sock *csk, u32 credits)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3018c2ecf20Sopenharmony_ci	struct cpl_rx_data_ack *req;
3028c2ecf20Sopenharmony_ci	u32 dack = F_RX_DACK_CHANGE | V_RX_DACK_MODE(1);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
3058c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u, credit %u, dack %u.\n",
3068c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid, credits, dack);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	skb = alloc_wr(sizeof(*req), 0, GFP_ATOMIC);
3098c2ecf20Sopenharmony_ci	if (!skb) {
3108c2ecf20Sopenharmony_ci		pr_info("csk 0x%p, credit %u, OOM.\n", csk, credits);
3118c2ecf20Sopenharmony_ci		return 0;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci	req = (struct cpl_rx_data_ack *)skb->head;
3148c2ecf20Sopenharmony_ci	req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
3158c2ecf20Sopenharmony_ci	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_RX_DATA_ACK, csk->tid));
3168c2ecf20Sopenharmony_ci	req->credit_dack = htonl(F_RX_DACK_CHANGE | V_RX_DACK_MODE(1) |
3178c2ecf20Sopenharmony_ci				V_RX_CREDITS(credits));
3188c2ecf20Sopenharmony_ci	skb->priority = CPL_PRIORITY_ACK;
3198c2ecf20Sopenharmony_ci	cxgb3_ofld_send(csk->cdev->lldev, skb);
3208c2ecf20Sopenharmony_ci	return credits;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/*
3248c2ecf20Sopenharmony_ci * CPL connection tx data: host ->
3258c2ecf20Sopenharmony_ci *
3268c2ecf20Sopenharmony_ci * Send iscsi PDU via TX_DATA CPL message. Returns the number of
3278c2ecf20Sopenharmony_ci * credits sent.
3288c2ecf20Sopenharmony_ci * Each TX_DATA consumes work request credit (wrs), so we need to keep track of
3298c2ecf20Sopenharmony_ci * how many we've used so far and how many are pending (i.e., yet ack'ed by T3).
3308c2ecf20Sopenharmony_ci */
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic unsigned int wrlen __read_mostly;
3338c2ecf20Sopenharmony_cistatic unsigned int skb_wrs[SKB_WR_LIST_SIZE] __read_mostly;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic void init_wr_tab(unsigned int wr_len)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	int i;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (skb_wrs[1])		/* already initialized */
3408c2ecf20Sopenharmony_ci		return;
3418c2ecf20Sopenharmony_ci	for (i = 1; i < SKB_WR_LIST_SIZE; i++) {
3428c2ecf20Sopenharmony_ci		int sgl_len = (3 * i) / 2 + (i & 1);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		sgl_len += 3;
3458c2ecf20Sopenharmony_ci		skb_wrs[i] = (sgl_len <= wr_len
3468c2ecf20Sopenharmony_ci			      ? 1 : 1 + (sgl_len - 2) / (wr_len - 1));
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	wrlen = wr_len * 8;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic inline void make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb,
3528c2ecf20Sopenharmony_ci				   int len, int req_completion)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct tx_data_wr *req;
3558c2ecf20Sopenharmony_ci	struct l2t_entry *l2t = csk->l2t;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
3588c2ecf20Sopenharmony_ci	req = __skb_push(skb, sizeof(*req));
3598c2ecf20Sopenharmony_ci	req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA) |
3608c2ecf20Sopenharmony_ci			(req_completion ? F_WR_COMPL : 0));
3618c2ecf20Sopenharmony_ci	req->wr_lo = htonl(V_WR_TID(csk->tid));
3628c2ecf20Sopenharmony_ci	/* len includes the length of any HW ULP additions */
3638c2ecf20Sopenharmony_ci	req->len = htonl(len);
3648c2ecf20Sopenharmony_ci	/* V_TX_ULP_SUBMODE sets both the mode and submode */
3658c2ecf20Sopenharmony_ci	req->flags = htonl(V_TX_ULP_SUBMODE(cxgbi_skcb_tx_ulp_mode(skb)) |
3668c2ecf20Sopenharmony_ci			   V_TX_SHOVE((skb_peek(&csk->write_queue) ? 0 : 1)));
3678c2ecf20Sopenharmony_ci	req->sndseq = htonl(csk->snd_nxt);
3688c2ecf20Sopenharmony_ci	req->param = htonl(V_TX_PORT(l2t->smt_idx));
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT)) {
3718c2ecf20Sopenharmony_ci		req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT |
3728c2ecf20Sopenharmony_ci				    V_TX_CPU_IDX(csk->rss_qid));
3738c2ecf20Sopenharmony_ci		/* sendbuffer is in units of 32KB. */
3748c2ecf20Sopenharmony_ci		req->param |= htonl(V_TX_SNDBUF(csk->snd_win >> 15));
3758c2ecf20Sopenharmony_ci		cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/*
3808c2ecf20Sopenharmony_ci * push_tx_frames -- start transmit
3818c2ecf20Sopenharmony_ci *
3828c2ecf20Sopenharmony_ci * Prepends TX_DATA_WR or CPL_CLOSE_CON_REQ headers to buffers waiting in a
3838c2ecf20Sopenharmony_ci * connection's send queue and sends them on to T3.  Must be called with the
3848c2ecf20Sopenharmony_ci * connection's lock held.  Returns the amount of send buffer space that was
3858c2ecf20Sopenharmony_ci * freed as a result of sending queued data to T3.
3868c2ecf20Sopenharmony_ci */
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic void arp_failure_skb_discard(struct t3cdev *dev, struct sk_buff *skb)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	kfree_skb(skb);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int push_tx_frames(struct cxgbi_sock *csk, int req_completion)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	int total_size = 0;
3968c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (unlikely(csk->state < CTP_ESTABLISHED ||
3998c2ecf20Sopenharmony_ci		csk->state == CTP_CLOSE_WAIT_1 || csk->state >= CTP_ABORTING)) {
4008c2ecf20Sopenharmony_ci			log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_TX,
4018c2ecf20Sopenharmony_ci				"csk 0x%p,%u,0x%lx,%u, in closing state.\n",
4028c2ecf20Sopenharmony_ci				csk, csk->state, csk->flags, csk->tid);
4038c2ecf20Sopenharmony_ci		return 0;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	while (csk->wr_cred && (skb = skb_peek(&csk->write_queue)) != NULL) {
4078c2ecf20Sopenharmony_ci		int len = skb->len;	/* length before skb_push */
4088c2ecf20Sopenharmony_ci		int frags = skb_shinfo(skb)->nr_frags + (len != skb->data_len);
4098c2ecf20Sopenharmony_ci		int wrs_needed = skb_wrs[frags];
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		if (wrs_needed > 1 && len + sizeof(struct tx_data_wr) <= wrlen)
4128c2ecf20Sopenharmony_ci			wrs_needed = 1;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		WARN_ON(frags >= SKB_WR_LIST_SIZE || wrs_needed < 1);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		if (csk->wr_cred < wrs_needed) {
4178c2ecf20Sopenharmony_ci			log_debug(1 << CXGBI_DBG_PDU_TX,
4188c2ecf20Sopenharmony_ci				"csk 0x%p, skb len %u/%u, frag %u, wr %d<%u.\n",
4198c2ecf20Sopenharmony_ci				csk, skb->len, skb->data_len, frags,
4208c2ecf20Sopenharmony_ci				wrs_needed, csk->wr_cred);
4218c2ecf20Sopenharmony_ci			break;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		__skb_unlink(skb, &csk->write_queue);
4258c2ecf20Sopenharmony_ci		skb->priority = CPL_PRIORITY_DATA;
4268c2ecf20Sopenharmony_ci		skb->csum = wrs_needed;	/* remember this until the WR_ACK */
4278c2ecf20Sopenharmony_ci		csk->wr_cred -= wrs_needed;
4288c2ecf20Sopenharmony_ci		csk->wr_una_cred += wrs_needed;
4298c2ecf20Sopenharmony_ci		cxgbi_sock_enqueue_wr(csk, skb);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_TX,
4328c2ecf20Sopenharmony_ci			"csk 0x%p, enqueue, skb len %u/%u, frag %u, wr %d, "
4338c2ecf20Sopenharmony_ci			"left %u, unack %u.\n",
4348c2ecf20Sopenharmony_ci			csk, skb->len, skb->data_len, frags, skb->csum,
4358c2ecf20Sopenharmony_ci			csk->wr_cred, csk->wr_una_cred);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		if (likely(cxgbi_skcb_test_flag(skb, SKCBF_TX_NEED_HDR))) {
4388c2ecf20Sopenharmony_ci			if ((req_completion &&
4398c2ecf20Sopenharmony_ci				csk->wr_una_cred == wrs_needed) ||
4408c2ecf20Sopenharmony_ci			     csk->wr_una_cred >= csk->wr_max_cred / 2) {
4418c2ecf20Sopenharmony_ci				req_completion = 1;
4428c2ecf20Sopenharmony_ci				csk->wr_una_cred = 0;
4438c2ecf20Sopenharmony_ci			}
4448c2ecf20Sopenharmony_ci			len += cxgbi_ulp_extra_len(cxgbi_skcb_tx_ulp_mode(skb));
4458c2ecf20Sopenharmony_ci			make_tx_data_wr(csk, skb, len, req_completion);
4468c2ecf20Sopenharmony_ci			csk->snd_nxt += len;
4478c2ecf20Sopenharmony_ci			cxgbi_skcb_clear_flag(skb, SKCBF_TX_NEED_HDR);
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci		total_size += skb->truesize;
4508c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_TX,
4518c2ecf20Sopenharmony_ci			"csk 0x%p, tid 0x%x, send skb 0x%p.\n",
4528c2ecf20Sopenharmony_ci			csk, csk->tid, skb);
4538c2ecf20Sopenharmony_ci		set_arp_failure_handler(skb, arp_failure_skb_discard);
4548c2ecf20Sopenharmony_ci		l2t_send(csk->cdev->lldev, skb, csk->l2t);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci	return total_size;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/*
4608c2ecf20Sopenharmony_ci * Process a CPL_ACT_ESTABLISH message: -> host
4618c2ecf20Sopenharmony_ci * Updates connection state from an active establish CPL message.  Runs with
4628c2ecf20Sopenharmony_ci * the connection lock held.
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic inline void free_atid(struct cxgbi_sock *csk)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	if (cxgbi_sock_flag(csk, CTPF_HAS_ATID)) {
4688c2ecf20Sopenharmony_ci		cxgb3_free_atid(csk->cdev->lldev, csk->atid);
4698c2ecf20Sopenharmony_ci		cxgbi_sock_clear_flag(csk, CTPF_HAS_ATID);
4708c2ecf20Sopenharmony_ci		cxgbi_sock_put(csk);
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic int do_act_establish(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = ctx;
4778c2ecf20Sopenharmony_ci	struct cpl_act_establish *req = cplhdr(skb);
4788c2ecf20Sopenharmony_ci	unsigned int tid = GET_TID(req);
4798c2ecf20Sopenharmony_ci	unsigned int atid = G_PASS_OPEN_TID(ntohl(req->tos_tid));
4808c2ecf20Sopenharmony_ci	u32 rcv_isn = ntohl(req->rcv_isn);	/* real RCV_ISN + 1 */
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
4838c2ecf20Sopenharmony_ci		"atid 0x%x,tid 0x%x, csk 0x%p,%u,0x%lx, isn %u.\n",
4848c2ecf20Sopenharmony_ci		atid, atid, csk, csk->state, csk->flags, rcv_isn);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
4878c2ecf20Sopenharmony_ci	cxgbi_sock_set_flag(csk, CTPF_HAS_TID);
4888c2ecf20Sopenharmony_ci	csk->tid = tid;
4898c2ecf20Sopenharmony_ci	cxgb3_insert_tid(csk->cdev->lldev, &t3_client, csk, tid);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	free_atid(csk);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	csk->rss_qid = G_QNUM(ntohs(skb->csum));
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
4968c2ecf20Sopenharmony_ci	if (csk->retry_timer.function) {
4978c2ecf20Sopenharmony_ci		del_timer(&csk->retry_timer);
4988c2ecf20Sopenharmony_ci		csk->retry_timer.function = NULL;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (unlikely(csk->state != CTP_ACTIVE_OPEN))
5028c2ecf20Sopenharmony_ci		pr_info("csk 0x%p,%u,0x%lx,%u, got EST.\n",
5038c2ecf20Sopenharmony_ci			csk, csk->state, csk->flags, csk->tid);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	csk->copied_seq = csk->rcv_wup = csk->rcv_nxt = rcv_isn;
5068c2ecf20Sopenharmony_ci	if (csk->rcv_win > (M_RCV_BUFSIZ << 10))
5078c2ecf20Sopenharmony_ci		csk->rcv_wup -= csk->rcv_win - (M_RCV_BUFSIZ << 10);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt));
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (unlikely(cxgbi_sock_flag(csk, CTPF_ACTIVE_CLOSE_NEEDED)))
5128c2ecf20Sopenharmony_ci		/* upper layer has requested closing */
5138c2ecf20Sopenharmony_ci		send_abort_req(csk);
5148c2ecf20Sopenharmony_ci	else {
5158c2ecf20Sopenharmony_ci		if (skb_queue_len(&csk->write_queue))
5168c2ecf20Sopenharmony_ci			push_tx_frames(csk, 1);
5178c2ecf20Sopenharmony_ci		cxgbi_conn_tx_open(csk);
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
5218c2ecf20Sopenharmony_ci	__kfree_skb(skb);
5228c2ecf20Sopenharmony_ci	return 0;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci/*
5268c2ecf20Sopenharmony_ci * Process a CPL_ACT_OPEN_RPL message: -> host
5278c2ecf20Sopenharmony_ci * Handle active open failures.
5288c2ecf20Sopenharmony_ci */
5298c2ecf20Sopenharmony_cistatic int act_open_rpl_status_to_errno(int status)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	switch (status) {
5328c2ecf20Sopenharmony_ci	case CPL_ERR_CONN_RESET:
5338c2ecf20Sopenharmony_ci		return -ECONNREFUSED;
5348c2ecf20Sopenharmony_ci	case CPL_ERR_ARP_MISS:
5358c2ecf20Sopenharmony_ci		return -EHOSTUNREACH;
5368c2ecf20Sopenharmony_ci	case CPL_ERR_CONN_TIMEDOUT:
5378c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
5388c2ecf20Sopenharmony_ci	case CPL_ERR_TCAM_FULL:
5398c2ecf20Sopenharmony_ci		return -ENOMEM;
5408c2ecf20Sopenharmony_ci	case CPL_ERR_CONN_EXIST:
5418c2ecf20Sopenharmony_ci		return -EADDRINUSE;
5428c2ecf20Sopenharmony_ci	default:
5438c2ecf20Sopenharmony_ci		return -EIO;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic void act_open_retry_timer(struct timer_list *t)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = from_timer(csk, t, retry_timer);
5508c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
5538c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u.\n",
5548c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
5578c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
5588c2ecf20Sopenharmony_ci	skb = alloc_wr(sizeof(struct cpl_act_open_req), 0, GFP_ATOMIC);
5598c2ecf20Sopenharmony_ci	if (!skb)
5608c2ecf20Sopenharmony_ci		cxgbi_sock_fail_act_open(csk, -ENOMEM);
5618c2ecf20Sopenharmony_ci	else {
5628c2ecf20Sopenharmony_ci		skb->sk = (struct sock *)csk;
5638c2ecf20Sopenharmony_ci		set_arp_failure_handler(skb, act_open_arp_failure);
5648c2ecf20Sopenharmony_ci		send_act_open_req(csk, skb, csk->l2t);
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
5678c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic int do_act_open_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = ctx;
5738c2ecf20Sopenharmony_ci	struct cpl_act_open_rpl *rpl = cplhdr(skb);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	pr_info("csk 0x%p,%u,0x%lx,%u, status %u, %pI4:%u-%pI4:%u.\n",
5768c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->atid, rpl->status,
5778c2ecf20Sopenharmony_ci		&csk->saddr.sin_addr.s_addr, ntohs(csk->saddr.sin_port),
5788c2ecf20Sopenharmony_ci		&csk->daddr.sin_addr.s_addr, ntohs(csk->daddr.sin_port));
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	if (rpl->status != CPL_ERR_TCAM_FULL &&
5818c2ecf20Sopenharmony_ci	    rpl->status != CPL_ERR_CONN_EXIST &&
5828c2ecf20Sopenharmony_ci	    rpl->status != CPL_ERR_ARP_MISS)
5838c2ecf20Sopenharmony_ci		cxgb3_queue_tid_release(tdev, GET_TID(rpl));
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
5868c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
5878c2ecf20Sopenharmony_ci	if (rpl->status == CPL_ERR_CONN_EXIST &&
5888c2ecf20Sopenharmony_ci	    csk->retry_timer.function != act_open_retry_timer) {
5898c2ecf20Sopenharmony_ci		csk->retry_timer.function = act_open_retry_timer;
5908c2ecf20Sopenharmony_ci		mod_timer(&csk->retry_timer, jiffies + HZ / 2);
5918c2ecf20Sopenharmony_ci	} else
5928c2ecf20Sopenharmony_ci		cxgbi_sock_fail_act_open(csk,
5938c2ecf20Sopenharmony_ci				act_open_rpl_status_to_errno(rpl->status));
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
5968c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
5978c2ecf20Sopenharmony_ci	__kfree_skb(skb);
5988c2ecf20Sopenharmony_ci	return 0;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci/*
6028c2ecf20Sopenharmony_ci * Process PEER_CLOSE CPL messages: -> host
6038c2ecf20Sopenharmony_ci * Handle peer FIN.
6048c2ecf20Sopenharmony_ci */
6058c2ecf20Sopenharmony_cistatic int do_peer_close(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = ctx;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
6108c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u.\n",
6118c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	cxgbi_sock_rcv_peer_close(csk);
6148c2ecf20Sopenharmony_ci	__kfree_skb(skb);
6158c2ecf20Sopenharmony_ci	return 0;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci/*
6198c2ecf20Sopenharmony_ci * Process CLOSE_CONN_RPL CPL message: -> host
6208c2ecf20Sopenharmony_ci * Process a peer ACK to our FIN.
6218c2ecf20Sopenharmony_ci */
6228c2ecf20Sopenharmony_cistatic int do_close_con_rpl(struct t3cdev *cdev, struct sk_buff *skb,
6238c2ecf20Sopenharmony_ci			    void *ctx)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = ctx;
6268c2ecf20Sopenharmony_ci	struct cpl_close_con_rpl *rpl = cplhdr(skb);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
6298c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u, snxt %u.\n",
6308c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid, ntohl(rpl->snd_nxt));
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	cxgbi_sock_rcv_close_conn_rpl(csk, ntohl(rpl->snd_nxt));
6338c2ecf20Sopenharmony_ci	__kfree_skb(skb);
6348c2ecf20Sopenharmony_ci	return 0;
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci/*
6388c2ecf20Sopenharmony_ci * Process ABORT_REQ_RSS CPL message: -> host
6398c2ecf20Sopenharmony_ci * Process abort requests.  If we are waiting for an ABORT_RPL we ignore this
6408c2ecf20Sopenharmony_ci * request except that we need to reply to it.
6418c2ecf20Sopenharmony_ci */
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic int abort_status_to_errno(struct cxgbi_sock *csk, int abort_reason,
6448c2ecf20Sopenharmony_ci				 int *need_rst)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	switch (abort_reason) {
6478c2ecf20Sopenharmony_ci	case CPL_ERR_BAD_SYN:
6488c2ecf20Sopenharmony_ci	case CPL_ERR_CONN_RESET:
6498c2ecf20Sopenharmony_ci		return csk->state > CTP_ESTABLISHED ? -EPIPE : -ECONNRESET;
6508c2ecf20Sopenharmony_ci	case CPL_ERR_XMIT_TIMEDOUT:
6518c2ecf20Sopenharmony_ci	case CPL_ERR_PERSIST_TIMEDOUT:
6528c2ecf20Sopenharmony_ci	case CPL_ERR_FINWAIT2_TIMEDOUT:
6538c2ecf20Sopenharmony_ci	case CPL_ERR_KEEPALIVE_TIMEDOUT:
6548c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
6558c2ecf20Sopenharmony_ci	default:
6568c2ecf20Sopenharmony_ci		return -EIO;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int do_abort_req(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	const struct cpl_abort_req_rss *req = cplhdr(skb);
6638c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = ctx;
6648c2ecf20Sopenharmony_ci	int rst_status = CPL_ABORT_NO_RST;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
6678c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u.\n",
6688c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (req->status == CPL_ERR_RTX_NEG_ADVICE ||
6718c2ecf20Sopenharmony_ci	    req->status == CPL_ERR_PERSIST_NEG_ADVICE) {
6728c2ecf20Sopenharmony_ci		goto done;
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
6768c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (!cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD)) {
6798c2ecf20Sopenharmony_ci		cxgbi_sock_set_flag(csk, CTPF_ABORT_REQ_RCVD);
6808c2ecf20Sopenharmony_ci		cxgbi_sock_set_state(csk, CTP_ABORTING);
6818c2ecf20Sopenharmony_ci		goto out;
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	cxgbi_sock_clear_flag(csk, CTPF_ABORT_REQ_RCVD);
6858c2ecf20Sopenharmony_ci	send_abort_rpl(csk, rst_status);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if (!cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) {
6888c2ecf20Sopenharmony_ci		csk->err = abort_status_to_errno(csk, req->status, &rst_status);
6898c2ecf20Sopenharmony_ci		cxgbi_sock_closed(csk);
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ciout:
6938c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
6948c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
6958c2ecf20Sopenharmony_cidone:
6968c2ecf20Sopenharmony_ci	__kfree_skb(skb);
6978c2ecf20Sopenharmony_ci	return 0;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci/*
7018c2ecf20Sopenharmony_ci * Process ABORT_RPL_RSS CPL message: -> host
7028c2ecf20Sopenharmony_ci * Process abort replies.  We only process these messages if we anticipate
7038c2ecf20Sopenharmony_ci * them as the coordination between SW and HW in this area is somewhat lacking
7048c2ecf20Sopenharmony_ci * and sometimes we get ABORT_RPLs after we are done with the connection that
7058c2ecf20Sopenharmony_ci * originated the ABORT_REQ.
7068c2ecf20Sopenharmony_ci */
7078c2ecf20Sopenharmony_cistatic int do_abort_rpl(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	struct cpl_abort_rpl_rss *rpl = cplhdr(skb);
7108c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = ctx;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
7138c2ecf20Sopenharmony_ci		"status 0x%x, csk 0x%p, s %u, 0x%lx.\n",
7148c2ecf20Sopenharmony_ci		rpl->status, csk, csk ? csk->state : 0,
7158c2ecf20Sopenharmony_ci		csk ? csk->flags : 0UL);
7168c2ecf20Sopenharmony_ci	/*
7178c2ecf20Sopenharmony_ci	 * Ignore replies to post-close aborts indicating that the abort was
7188c2ecf20Sopenharmony_ci	 * requested too late.  These connections are terminated when we get
7198c2ecf20Sopenharmony_ci	 * PEER_CLOSE or CLOSE_CON_RPL and by the time the abort_rpl_rss
7208c2ecf20Sopenharmony_ci	 * arrives the TID is either no longer used or it has been recycled.
7218c2ecf20Sopenharmony_ci	 */
7228c2ecf20Sopenharmony_ci	if (rpl->status == CPL_ERR_ABORT_FAILED)
7238c2ecf20Sopenharmony_ci		goto rel_skb;
7248c2ecf20Sopenharmony_ci	/*
7258c2ecf20Sopenharmony_ci	 * Sometimes we've already closed the connection, e.g., a post-close
7268c2ecf20Sopenharmony_ci	 * abort races with ABORT_REQ_RSS, the latter frees the connection
7278c2ecf20Sopenharmony_ci	 * expecting the ABORT_REQ will fail with CPL_ERR_ABORT_FAILED,
7288c2ecf20Sopenharmony_ci	 * but FW turns the ABORT_REQ into a regular one and so we get
7298c2ecf20Sopenharmony_ci	 * ABORT_RPL_RSS with status 0 and no connection.
7308c2ecf20Sopenharmony_ci	 */
7318c2ecf20Sopenharmony_ci	if (csk)
7328c2ecf20Sopenharmony_ci		cxgbi_sock_rcv_abort_rpl(csk);
7338c2ecf20Sopenharmony_cirel_skb:
7348c2ecf20Sopenharmony_ci	__kfree_skb(skb);
7358c2ecf20Sopenharmony_ci	return 0;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci/*
7398c2ecf20Sopenharmony_ci * Process RX_ISCSI_HDR CPL message: -> host
7408c2ecf20Sopenharmony_ci * Handle received PDUs, the payload could be DDP'ed. If not, the payload
7418c2ecf20Sopenharmony_ci * follow after the bhs.
7428c2ecf20Sopenharmony_ci */
7438c2ecf20Sopenharmony_cistatic int do_iscsi_hdr(struct t3cdev *t3dev, struct sk_buff *skb, void *ctx)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = ctx;
7468c2ecf20Sopenharmony_ci	struct cpl_iscsi_hdr *hdr_cpl = cplhdr(skb);
7478c2ecf20Sopenharmony_ci	struct cpl_iscsi_hdr_norss data_cpl;
7488c2ecf20Sopenharmony_ci	struct cpl_rx_data_ddp_norss ddp_cpl;
7498c2ecf20Sopenharmony_ci	unsigned int hdr_len, data_len, status;
7508c2ecf20Sopenharmony_ci	unsigned int len;
7518c2ecf20Sopenharmony_ci	int err;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
7548c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u, skb 0x%p,%u.\n",
7558c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid, skb, skb->len);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (unlikely(csk->state >= CTP_PASSIVE_CLOSE)) {
7608c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
7618c2ecf20Sopenharmony_ci			"csk 0x%p,%u,0x%lx,%u, bad state.\n",
7628c2ecf20Sopenharmony_ci			csk, csk->state, csk->flags, csk->tid);
7638c2ecf20Sopenharmony_ci		if (csk->state != CTP_ABORTING)
7648c2ecf20Sopenharmony_ci			goto abort_conn;
7658c2ecf20Sopenharmony_ci		else
7668c2ecf20Sopenharmony_ci			goto discard;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	cxgbi_skcb_tcp_seq(skb) = ntohl(hdr_cpl->seq);
7708c2ecf20Sopenharmony_ci	cxgbi_skcb_flags(skb) = 0;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
7738c2ecf20Sopenharmony_ci	__skb_pull(skb, sizeof(struct cpl_iscsi_hdr));
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	len = hdr_len = ntohs(hdr_cpl->len);
7768c2ecf20Sopenharmony_ci	/* msg coalesce is off or not enough data received */
7778c2ecf20Sopenharmony_ci	if (skb->len <= hdr_len) {
7788c2ecf20Sopenharmony_ci		pr_err("%s: tid %u, CPL_ISCSI_HDR, skb len %u < %u.\n",
7798c2ecf20Sopenharmony_ci			csk->cdev->ports[csk->port_id]->name, csk->tid,
7808c2ecf20Sopenharmony_ci			skb->len, hdr_len);
7818c2ecf20Sopenharmony_ci		goto abort_conn;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci	cxgbi_skcb_set_flag(skb, SKCBF_RX_COALESCED);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	err = skb_copy_bits(skb, skb->len - sizeof(ddp_cpl), &ddp_cpl,
7868c2ecf20Sopenharmony_ci			    sizeof(ddp_cpl));
7878c2ecf20Sopenharmony_ci	if (err < 0) {
7888c2ecf20Sopenharmony_ci		pr_err("%s: tid %u, copy cpl_ddp %u-%zu failed %d.\n",
7898c2ecf20Sopenharmony_ci			csk->cdev->ports[csk->port_id]->name, csk->tid,
7908c2ecf20Sopenharmony_ci			skb->len, sizeof(ddp_cpl), err);
7918c2ecf20Sopenharmony_ci		goto abort_conn;
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	cxgbi_skcb_set_flag(skb, SKCBF_RX_STATUS);
7958c2ecf20Sopenharmony_ci	cxgbi_skcb_rx_pdulen(skb) = ntohs(ddp_cpl.len);
7968c2ecf20Sopenharmony_ci	cxgbi_skcb_rx_ddigest(skb) = ntohl(ddp_cpl.ulp_crc);
7978c2ecf20Sopenharmony_ci	status = ntohl(ddp_cpl.ddp_status);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
8008c2ecf20Sopenharmony_ci		"csk 0x%p, skb 0x%p,%u, pdulen %u, status 0x%x.\n",
8018c2ecf20Sopenharmony_ci		csk, skb, skb->len, cxgbi_skcb_rx_pdulen(skb), status);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	if (status & (1 << CPL_RX_DDP_STATUS_HCRC_SHIFT))
8048c2ecf20Sopenharmony_ci		cxgbi_skcb_set_flag(skb, SKCBF_RX_HCRC_ERR);
8058c2ecf20Sopenharmony_ci	if (status & (1 << CPL_RX_DDP_STATUS_DCRC_SHIFT))
8068c2ecf20Sopenharmony_ci		cxgbi_skcb_set_flag(skb, SKCBF_RX_DCRC_ERR);
8078c2ecf20Sopenharmony_ci	if (status & (1 << CPL_RX_DDP_STATUS_PAD_SHIFT))
8088c2ecf20Sopenharmony_ci		cxgbi_skcb_set_flag(skb, SKCBF_RX_PAD_ERR);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (skb->len > (hdr_len + sizeof(ddp_cpl))) {
8118c2ecf20Sopenharmony_ci		err = skb_copy_bits(skb, hdr_len, &data_cpl, sizeof(data_cpl));
8128c2ecf20Sopenharmony_ci		if (err < 0) {
8138c2ecf20Sopenharmony_ci			pr_err("%s: tid %u, cp %zu/%u failed %d.\n",
8148c2ecf20Sopenharmony_ci				csk->cdev->ports[csk->port_id]->name,
8158c2ecf20Sopenharmony_ci				csk->tid, sizeof(data_cpl), skb->len, err);
8168c2ecf20Sopenharmony_ci			goto abort_conn;
8178c2ecf20Sopenharmony_ci		}
8188c2ecf20Sopenharmony_ci		data_len = ntohs(data_cpl.len);
8198c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_DDP | 1 << CXGBI_DBG_PDU_RX,
8208c2ecf20Sopenharmony_ci			"skb 0x%p, pdu not ddp'ed %u/%u, status 0x%x.\n",
8218c2ecf20Sopenharmony_ci			skb, data_len, cxgbi_skcb_rx_pdulen(skb), status);
8228c2ecf20Sopenharmony_ci		len += sizeof(data_cpl) + data_len;
8238c2ecf20Sopenharmony_ci	} else if (status & (1 << CPL_RX_DDP_STATUS_DDP_SHIFT))
8248c2ecf20Sopenharmony_ci		cxgbi_skcb_set_flag(skb, SKCBF_RX_DATA_DDPD);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	csk->rcv_nxt = ntohl(ddp_cpl.seq) + cxgbi_skcb_rx_pdulen(skb);
8278c2ecf20Sopenharmony_ci	__pskb_trim(skb, len);
8288c2ecf20Sopenharmony_ci	__skb_queue_tail(&csk->receive_queue, skb);
8298c2ecf20Sopenharmony_ci	cxgbi_conn_pdu_ready(csk);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
8328c2ecf20Sopenharmony_ci	return 0;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ciabort_conn:
8358c2ecf20Sopenharmony_ci	send_abort_req(csk);
8368c2ecf20Sopenharmony_cidiscard:
8378c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
8388c2ecf20Sopenharmony_ci	__kfree_skb(skb);
8398c2ecf20Sopenharmony_ci	return 0;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci/*
8438c2ecf20Sopenharmony_ci * Process TX_DATA_ACK CPL messages: -> host
8448c2ecf20Sopenharmony_ci * Process an acknowledgment of WR completion.  Advance snd_una and send the
8458c2ecf20Sopenharmony_ci * next batch of work requests from the write queue.
8468c2ecf20Sopenharmony_ci */
8478c2ecf20Sopenharmony_cistatic int do_wr_ack(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = ctx;
8508c2ecf20Sopenharmony_ci	struct cpl_wr_ack *hdr = cplhdr(skb);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
8538c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u, cr %u.\n",
8548c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid, ntohs(hdr->credits));
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	cxgbi_sock_rcv_wr_ack(csk, ntohs(hdr->credits), ntohl(hdr->snd_una), 1);
8578c2ecf20Sopenharmony_ci	__kfree_skb(skb);
8588c2ecf20Sopenharmony_ci	return 0;
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci/*
8628c2ecf20Sopenharmony_ci * for each connection, pre-allocate skbs needed for close/abort requests. So
8638c2ecf20Sopenharmony_ci * that we can service the request right away.
8648c2ecf20Sopenharmony_ci */
8658c2ecf20Sopenharmony_cistatic int alloc_cpls(struct cxgbi_sock *csk)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	csk->cpl_close = alloc_wr(sizeof(struct cpl_close_con_req), 0,
8688c2ecf20Sopenharmony_ci					GFP_KERNEL);
8698c2ecf20Sopenharmony_ci	if (!csk->cpl_close)
8708c2ecf20Sopenharmony_ci		return -ENOMEM;
8718c2ecf20Sopenharmony_ci	csk->cpl_abort_req = alloc_wr(sizeof(struct cpl_abort_req), 0,
8728c2ecf20Sopenharmony_ci					GFP_KERNEL);
8738c2ecf20Sopenharmony_ci	if (!csk->cpl_abort_req)
8748c2ecf20Sopenharmony_ci		goto free_cpl_skbs;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	csk->cpl_abort_rpl = alloc_wr(sizeof(struct cpl_abort_rpl), 0,
8778c2ecf20Sopenharmony_ci					GFP_KERNEL);
8788c2ecf20Sopenharmony_ci	if (!csk->cpl_abort_rpl)
8798c2ecf20Sopenharmony_ci		goto free_cpl_skbs;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	return 0;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cifree_cpl_skbs:
8848c2ecf20Sopenharmony_ci	cxgbi_sock_free_cpl_skbs(csk);
8858c2ecf20Sopenharmony_ci	return -ENOMEM;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cistatic void l2t_put(struct cxgbi_sock *csk)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	struct t3cdev *t3dev = (struct t3cdev *)csk->cdev->lldev;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if (csk->l2t) {
8938c2ecf20Sopenharmony_ci		l2t_release(t3dev, csk->l2t);
8948c2ecf20Sopenharmony_ci		csk->l2t = NULL;
8958c2ecf20Sopenharmony_ci		cxgbi_sock_put(csk);
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci/*
9008c2ecf20Sopenharmony_ci * release_offload_resources - release offload resource
9018c2ecf20Sopenharmony_ci * Release resources held by an offload connection (TID, L2T entry, etc.)
9028c2ecf20Sopenharmony_ci */
9038c2ecf20Sopenharmony_cistatic void release_offload_resources(struct cxgbi_sock *csk)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct t3cdev *t3dev = (struct t3cdev *)csk->cdev->lldev;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
9088c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u.\n",
9098c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	csk->rss_qid = 0;
9128c2ecf20Sopenharmony_ci	cxgbi_sock_free_cpl_skbs(csk);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (csk->wr_cred != csk->wr_max_cred) {
9158c2ecf20Sopenharmony_ci		cxgbi_sock_purge_wr_queue(csk);
9168c2ecf20Sopenharmony_ci		cxgbi_sock_reset_wr_list(csk);
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci	l2t_put(csk);
9198c2ecf20Sopenharmony_ci	if (cxgbi_sock_flag(csk, CTPF_HAS_ATID))
9208c2ecf20Sopenharmony_ci		free_atid(csk);
9218c2ecf20Sopenharmony_ci	else if (cxgbi_sock_flag(csk, CTPF_HAS_TID)) {
9228c2ecf20Sopenharmony_ci		cxgb3_remove_tid(t3dev, (void *)csk, csk->tid);
9238c2ecf20Sopenharmony_ci		cxgbi_sock_clear_flag(csk, CTPF_HAS_TID);
9248c2ecf20Sopenharmony_ci		cxgbi_sock_put(csk);
9258c2ecf20Sopenharmony_ci	}
9268c2ecf20Sopenharmony_ci	csk->dst = NULL;
9278c2ecf20Sopenharmony_ci	csk->cdev = NULL;
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_cistatic void update_address(struct cxgbi_hba *chba)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	if (chba->ipv4addr) {
9338c2ecf20Sopenharmony_ci		if (chba->vdev &&
9348c2ecf20Sopenharmony_ci		    chba->ipv4addr != cxgb3i_get_private_ipv4addr(chba->vdev)) {
9358c2ecf20Sopenharmony_ci			cxgb3i_set_private_ipv4addr(chba->vdev, chba->ipv4addr);
9368c2ecf20Sopenharmony_ci			cxgb3i_set_private_ipv4addr(chba->ndev, 0);
9378c2ecf20Sopenharmony_ci			pr_info("%s set %pI4.\n",
9388c2ecf20Sopenharmony_ci				chba->vdev->name, &chba->ipv4addr);
9398c2ecf20Sopenharmony_ci		} else if (chba->ipv4addr !=
9408c2ecf20Sopenharmony_ci				cxgb3i_get_private_ipv4addr(chba->ndev)) {
9418c2ecf20Sopenharmony_ci			cxgb3i_set_private_ipv4addr(chba->ndev, chba->ipv4addr);
9428c2ecf20Sopenharmony_ci			pr_info("%s set %pI4.\n",
9438c2ecf20Sopenharmony_ci				chba->ndev->name, &chba->ipv4addr);
9448c2ecf20Sopenharmony_ci		}
9458c2ecf20Sopenharmony_ci	} else if (cxgb3i_get_private_ipv4addr(chba->ndev)) {
9468c2ecf20Sopenharmony_ci		if (chba->vdev)
9478c2ecf20Sopenharmony_ci			cxgb3i_set_private_ipv4addr(chba->vdev, 0);
9488c2ecf20Sopenharmony_ci		cxgb3i_set_private_ipv4addr(chba->ndev, 0);
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic int init_act_open(struct cxgbi_sock *csk)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	struct dst_entry *dst = csk->dst;
9558c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = csk->cdev;
9568c2ecf20Sopenharmony_ci	struct t3cdev *t3dev = (struct t3cdev *)cdev->lldev;
9578c2ecf20Sopenharmony_ci	struct net_device *ndev = cdev->ports[csk->port_id];
9588c2ecf20Sopenharmony_ci	struct cxgbi_hba *chba = cdev->hbas[csk->port_id];
9598c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
9608c2ecf20Sopenharmony_ci	int ret;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
9638c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx.\n", csk, csk->state, csk->flags);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	update_address(chba);
9668c2ecf20Sopenharmony_ci	if (chba->ipv4addr)
9678c2ecf20Sopenharmony_ci		csk->saddr.sin_addr.s_addr = chba->ipv4addr;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	csk->rss_qid = 0;
9708c2ecf20Sopenharmony_ci	csk->l2t = t3_l2t_get(t3dev, dst, ndev,
9718c2ecf20Sopenharmony_ci			      &csk->daddr.sin_addr.s_addr);
9728c2ecf20Sopenharmony_ci	if (!csk->l2t) {
9738c2ecf20Sopenharmony_ci		pr_err("NO l2t available.\n");
9748c2ecf20Sopenharmony_ci		return -EINVAL;
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	csk->atid = cxgb3_alloc_atid(t3dev, &t3_client, csk);
9798c2ecf20Sopenharmony_ci	if (csk->atid < 0) {
9808c2ecf20Sopenharmony_ci		pr_err("NO atid available.\n");
9818c2ecf20Sopenharmony_ci		ret = -EINVAL;
9828c2ecf20Sopenharmony_ci		goto put_sock;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci	cxgbi_sock_set_flag(csk, CTPF_HAS_ATID);
9858c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	skb = alloc_wr(sizeof(struct cpl_act_open_req), 0, GFP_KERNEL);
9888c2ecf20Sopenharmony_ci	if (!skb) {
9898c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9908c2ecf20Sopenharmony_ci		goto free_atid;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci	skb->sk = (struct sock *)csk;
9938c2ecf20Sopenharmony_ci	set_arp_failure_handler(skb, act_open_arp_failure);
9948c2ecf20Sopenharmony_ci	csk->snd_win = cxgb3i_snd_win;
9958c2ecf20Sopenharmony_ci	csk->rcv_win = cxgb3i_rcv_win;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	csk->wr_max_cred = csk->wr_cred = T3C_DATA(t3dev)->max_wrs - 1;
9988c2ecf20Sopenharmony_ci	csk->wr_una_cred = 0;
9998c2ecf20Sopenharmony_ci	csk->mss_idx = cxgbi_sock_select_mss(csk, dst_mtu(dst));
10008c2ecf20Sopenharmony_ci	cxgbi_sock_reset_wr_list(csk);
10018c2ecf20Sopenharmony_ci	csk->err = 0;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
10048c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx, %pI4:%u-%pI4:%u.\n",
10058c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags,
10068c2ecf20Sopenharmony_ci		&csk->saddr.sin_addr.s_addr, ntohs(csk->saddr.sin_port),
10078c2ecf20Sopenharmony_ci		&csk->daddr.sin_addr.s_addr, ntohs(csk->daddr.sin_port));
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	cxgbi_sock_set_state(csk, CTP_ACTIVE_OPEN);
10108c2ecf20Sopenharmony_ci	send_act_open_req(csk, skb, csk->l2t);
10118c2ecf20Sopenharmony_ci	return 0;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cifree_atid:
10148c2ecf20Sopenharmony_ci	cxgb3_free_atid(t3dev, csk->atid);
10158c2ecf20Sopenharmony_ciput_sock:
10168c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
10178c2ecf20Sopenharmony_ci	l2t_release(t3dev, csk->l2t);
10188c2ecf20Sopenharmony_ci	csk->l2t = NULL;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	return ret;
10218c2ecf20Sopenharmony_ci}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_cicxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS] = {
10248c2ecf20Sopenharmony_ci	[CPL_ACT_ESTABLISH] = do_act_establish,
10258c2ecf20Sopenharmony_ci	[CPL_ACT_OPEN_RPL] = do_act_open_rpl,
10268c2ecf20Sopenharmony_ci	[CPL_PEER_CLOSE] = do_peer_close,
10278c2ecf20Sopenharmony_ci	[CPL_ABORT_REQ_RSS] = do_abort_req,
10288c2ecf20Sopenharmony_ci	[CPL_ABORT_RPL_RSS] = do_abort_rpl,
10298c2ecf20Sopenharmony_ci	[CPL_CLOSE_CON_RPL] = do_close_con_rpl,
10308c2ecf20Sopenharmony_ci	[CPL_TX_DMA_ACK] = do_wr_ack,
10318c2ecf20Sopenharmony_ci	[CPL_ISCSI_HDR] = do_iscsi_hdr,
10328c2ecf20Sopenharmony_ci};
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci/**
10358c2ecf20Sopenharmony_ci * cxgb3i_ofld_init - allocate and initialize resources for each adapter found
10368c2ecf20Sopenharmony_ci * @cdev:	cxgbi adapter
10378c2ecf20Sopenharmony_ci */
10388c2ecf20Sopenharmony_cistatic int cxgb3i_ofld_init(struct cxgbi_device *cdev)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	struct t3cdev *t3dev = (struct t3cdev *)cdev->lldev;
10418c2ecf20Sopenharmony_ci	struct adap_ports port;
10428c2ecf20Sopenharmony_ci	struct ofld_page_info rx_page_info;
10438c2ecf20Sopenharmony_ci	unsigned int wr_len;
10448c2ecf20Sopenharmony_ci	int rc;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if (t3dev->ctl(t3dev, GET_WR_LEN, &wr_len) < 0 ||
10478c2ecf20Sopenharmony_ci	    t3dev->ctl(t3dev, GET_PORTS, &port) < 0 ||
10488c2ecf20Sopenharmony_ci	    t3dev->ctl(t3dev, GET_RX_PAGE_INFO, &rx_page_info) < 0) {
10498c2ecf20Sopenharmony_ci		pr_warn("t3 0x%p, offload up, ioctl failed.\n", t3dev);
10508c2ecf20Sopenharmony_ci		return -EINVAL;
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	if (cxgb3i_max_connect > CXGBI_MAX_CONN)
10548c2ecf20Sopenharmony_ci		cxgb3i_max_connect = CXGBI_MAX_CONN;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	rc = cxgbi_device_portmap_create(cdev, cxgb3i_sport_base,
10578c2ecf20Sopenharmony_ci					cxgb3i_max_connect);
10588c2ecf20Sopenharmony_ci	if (rc < 0)
10598c2ecf20Sopenharmony_ci		return rc;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	init_wr_tab(wr_len);
10628c2ecf20Sopenharmony_ci	cdev->csk_release_offload_resources = release_offload_resources;
10638c2ecf20Sopenharmony_ci	cdev->csk_push_tx_frames = push_tx_frames;
10648c2ecf20Sopenharmony_ci	cdev->csk_send_abort_req = send_abort_req;
10658c2ecf20Sopenharmony_ci	cdev->csk_send_close_req = send_close_req;
10668c2ecf20Sopenharmony_ci	cdev->csk_send_rx_credits = send_rx_credits;
10678c2ecf20Sopenharmony_ci	cdev->csk_alloc_cpls = alloc_cpls;
10688c2ecf20Sopenharmony_ci	cdev->csk_init_act_open = init_act_open;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	pr_info("cdev 0x%p, offload up, added.\n", cdev);
10718c2ecf20Sopenharmony_ci	return 0;
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci/*
10758c2ecf20Sopenharmony_ci * functions to program the pagepod in h/w
10768c2ecf20Sopenharmony_ci */
10778c2ecf20Sopenharmony_cistatic inline void ulp_mem_io_set_hdr(struct sk_buff *skb, unsigned int addr)
10788c2ecf20Sopenharmony_ci{
10798c2ecf20Sopenharmony_ci	struct ulp_mem_io *req = (struct ulp_mem_io *)skb->head;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	memset(req, 0, sizeof(*req));
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_BYPASS));
10848c2ecf20Sopenharmony_ci	req->cmd_lock_addr = htonl(V_ULP_MEMIO_ADDR(addr >> 5) |
10858c2ecf20Sopenharmony_ci				   V_ULPTX_CMD(ULP_MEM_WRITE));
10868c2ecf20Sopenharmony_ci	req->len = htonl(V_ULP_MEMIO_DATA_LEN(IPPOD_SIZE >> 5) |
10878c2ecf20Sopenharmony_ci			 V_ULPTX_NFLITS((IPPOD_SIZE >> 3) + 1));
10888c2ecf20Sopenharmony_ci}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_cistatic struct cxgbi_ppm *cdev2ppm(struct cxgbi_device *cdev)
10918c2ecf20Sopenharmony_ci{
10928c2ecf20Sopenharmony_ci	return ((struct t3cdev *)cdev->lldev)->ulp_iscsi;
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cistatic int ddp_set_map(struct cxgbi_ppm *ppm, struct cxgbi_sock *csk,
10968c2ecf20Sopenharmony_ci		       struct cxgbi_task_tag_info *ttinfo)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	unsigned int idx = ttinfo->idx;
10998c2ecf20Sopenharmony_ci	unsigned int npods = ttinfo->npods;
11008c2ecf20Sopenharmony_ci	struct scatterlist *sg = ttinfo->sgl;
11018c2ecf20Sopenharmony_ci	struct cxgbi_pagepod *ppod;
11028c2ecf20Sopenharmony_ci	struct ulp_mem_io *req;
11038c2ecf20Sopenharmony_ci	unsigned int sg_off;
11048c2ecf20Sopenharmony_ci	unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit;
11058c2ecf20Sopenharmony_ci	int i;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	for (i = 0; i < npods; i++, idx++, pm_addr += IPPOD_SIZE) {
11088c2ecf20Sopenharmony_ci		struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) +
11098c2ecf20Sopenharmony_ci					       IPPOD_SIZE, 0, GFP_ATOMIC);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci		if (!skb)
11128c2ecf20Sopenharmony_ci			return -ENOMEM;
11138c2ecf20Sopenharmony_ci		ulp_mem_io_set_hdr(skb, pm_addr);
11148c2ecf20Sopenharmony_ci		req = (struct ulp_mem_io *)skb->head;
11158c2ecf20Sopenharmony_ci		ppod = (struct cxgbi_pagepod *)(req + 1);
11168c2ecf20Sopenharmony_ci		sg_off = i * PPOD_PAGES_MAX;
11178c2ecf20Sopenharmony_ci		cxgbi_ddp_set_one_ppod(ppod, ttinfo, &sg,
11188c2ecf20Sopenharmony_ci				       &sg_off);
11198c2ecf20Sopenharmony_ci		skb->priority = CPL_PRIORITY_CONTROL;
11208c2ecf20Sopenharmony_ci		cxgb3_ofld_send(ppm->lldev, skb);
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ci	return 0;
11238c2ecf20Sopenharmony_ci}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_cistatic void ddp_clear_map(struct cxgbi_device *cdev, struct cxgbi_ppm *ppm,
11268c2ecf20Sopenharmony_ci			  struct cxgbi_task_tag_info *ttinfo)
11278c2ecf20Sopenharmony_ci{
11288c2ecf20Sopenharmony_ci	unsigned int idx = ttinfo->idx;
11298c2ecf20Sopenharmony_ci	unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit;
11308c2ecf20Sopenharmony_ci	unsigned int npods = ttinfo->npods;
11318c2ecf20Sopenharmony_ci	int i;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DDP,
11348c2ecf20Sopenharmony_ci		  "cdev 0x%p, clear idx %u, npods %u.\n",
11358c2ecf20Sopenharmony_ci		  cdev, idx, npods);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	for (i = 0; i < npods; i++, idx++, pm_addr += IPPOD_SIZE) {
11388c2ecf20Sopenharmony_ci		struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) +
11398c2ecf20Sopenharmony_ci					       IPPOD_SIZE, 0, GFP_ATOMIC);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci		if (!skb) {
11428c2ecf20Sopenharmony_ci			pr_err("cdev 0x%p, clear ddp, %u,%d/%u, skb OOM.\n",
11438c2ecf20Sopenharmony_ci			       cdev, idx, i, npods);
11448c2ecf20Sopenharmony_ci			continue;
11458c2ecf20Sopenharmony_ci		}
11468c2ecf20Sopenharmony_ci		ulp_mem_io_set_hdr(skb, pm_addr);
11478c2ecf20Sopenharmony_ci		skb->priority = CPL_PRIORITY_CONTROL;
11488c2ecf20Sopenharmony_ci		cxgb3_ofld_send(ppm->lldev, skb);
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_cistatic int ddp_setup_conn_pgidx(struct cxgbi_sock *csk,
11538c2ecf20Sopenharmony_ci				unsigned int tid, int pg_idx)
11548c2ecf20Sopenharmony_ci{
11558c2ecf20Sopenharmony_ci	struct sk_buff *skb = alloc_wr(sizeof(struct cpl_set_tcb_field), 0,
11568c2ecf20Sopenharmony_ci					GFP_KERNEL);
11578c2ecf20Sopenharmony_ci	struct cpl_set_tcb_field *req;
11588c2ecf20Sopenharmony_ci	u64 val = pg_idx < DDP_PGIDX_MAX ? pg_idx : 0;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DDP,
11618c2ecf20Sopenharmony_ci		"csk 0x%p, tid %u, pg_idx %d.\n", csk, tid, pg_idx);
11628c2ecf20Sopenharmony_ci	if (!skb)
11638c2ecf20Sopenharmony_ci		return -ENOMEM;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	/* set up ulp submode and page size */
11668c2ecf20Sopenharmony_ci	req = (struct cpl_set_tcb_field *)skb->head;
11678c2ecf20Sopenharmony_ci	req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
11688c2ecf20Sopenharmony_ci	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
11698c2ecf20Sopenharmony_ci	req->reply = V_NO_REPLY(1);
11708c2ecf20Sopenharmony_ci	req->cpu_idx = 0;
11718c2ecf20Sopenharmony_ci	req->word = htons(31);
11728c2ecf20Sopenharmony_ci	req->mask = cpu_to_be64(0xF0000000);
11738c2ecf20Sopenharmony_ci	req->val = cpu_to_be64(val << 28);
11748c2ecf20Sopenharmony_ci	skb->priority = CPL_PRIORITY_CONTROL;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	cxgb3_ofld_send(csk->cdev->lldev, skb);
11778c2ecf20Sopenharmony_ci	return 0;
11788c2ecf20Sopenharmony_ci}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci/**
11818c2ecf20Sopenharmony_ci * cxgb3i_setup_conn_digest - setup conn. digest setting
11828c2ecf20Sopenharmony_ci * @csk: cxgb tcp socket
11838c2ecf20Sopenharmony_ci * @tid: connection id
11848c2ecf20Sopenharmony_ci * @hcrc: header digest enabled
11858c2ecf20Sopenharmony_ci * @dcrc: data digest enabled
11868c2ecf20Sopenharmony_ci * set up the iscsi digest settings for a connection identified by tid
11878c2ecf20Sopenharmony_ci */
11888c2ecf20Sopenharmony_cistatic int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid,
11898c2ecf20Sopenharmony_ci				 int hcrc, int dcrc)
11908c2ecf20Sopenharmony_ci{
11918c2ecf20Sopenharmony_ci	struct sk_buff *skb = alloc_wr(sizeof(struct cpl_set_tcb_field), 0,
11928c2ecf20Sopenharmony_ci					GFP_KERNEL);
11938c2ecf20Sopenharmony_ci	struct cpl_set_tcb_field *req;
11948c2ecf20Sopenharmony_ci	u64 val = (hcrc ? 1 : 0) | (dcrc ? 2 : 0);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DDP,
11978c2ecf20Sopenharmony_ci		"csk 0x%p, tid %u, crc %d,%d.\n", csk, tid, hcrc, dcrc);
11988c2ecf20Sopenharmony_ci	if (!skb)
11998c2ecf20Sopenharmony_ci		return -ENOMEM;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	/* set up ulp submode and page size */
12028c2ecf20Sopenharmony_ci	req = (struct cpl_set_tcb_field *)skb->head;
12038c2ecf20Sopenharmony_ci	req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
12048c2ecf20Sopenharmony_ci	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
12058c2ecf20Sopenharmony_ci	req->reply = V_NO_REPLY(1);
12068c2ecf20Sopenharmony_ci	req->cpu_idx = 0;
12078c2ecf20Sopenharmony_ci	req->word = htons(31);
12088c2ecf20Sopenharmony_ci	req->mask = cpu_to_be64(0x0F000000);
12098c2ecf20Sopenharmony_ci	req->val = cpu_to_be64(val << 24);
12108c2ecf20Sopenharmony_ci	skb->priority = CPL_PRIORITY_CONTROL;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	cxgb3_ofld_send(csk->cdev->lldev, skb);
12138c2ecf20Sopenharmony_ci	return 0;
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci/**
12178c2ecf20Sopenharmony_ci * cxgb3i_ddp_init - initialize the cxgb3 adapter's ddp resource
12188c2ecf20Sopenharmony_ci * @cdev: cxgb3i adapter
12198c2ecf20Sopenharmony_ci * initialize the ddp pagepod manager for a given adapter
12208c2ecf20Sopenharmony_ci */
12218c2ecf20Sopenharmony_cistatic int cxgb3i_ddp_init(struct cxgbi_device *cdev)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	struct t3cdev *tdev = (struct t3cdev *)cdev->lldev;
12248c2ecf20Sopenharmony_ci	struct net_device *ndev = cdev->ports[0];
12258c2ecf20Sopenharmony_ci	struct cxgbi_tag_format tformat;
12268c2ecf20Sopenharmony_ci	unsigned int ppmax, tagmask = 0;
12278c2ecf20Sopenharmony_ci	struct ulp_iscsi_info uinfo;
12288c2ecf20Sopenharmony_ci	int i, err;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	err = tdev->ctl(tdev, ULP_ISCSI_GET_PARAMS, &uinfo);
12318c2ecf20Sopenharmony_ci	if (err < 0) {
12328c2ecf20Sopenharmony_ci		pr_err("%s, failed to get iscsi param %d.\n",
12338c2ecf20Sopenharmony_ci		       ndev->name, err);
12348c2ecf20Sopenharmony_ci		return err;
12358c2ecf20Sopenharmony_ci	}
12368c2ecf20Sopenharmony_ci	if (uinfo.llimit >= uinfo.ulimit) {
12378c2ecf20Sopenharmony_ci		pr_warn("T3 %s, iscsi NOT enabled %u ~ %u!\n",
12388c2ecf20Sopenharmony_ci			ndev->name, uinfo.llimit, uinfo.ulimit);
12398c2ecf20Sopenharmony_ci		return -EACCES;
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	ppmax = (uinfo.ulimit - uinfo.llimit + 1) >> PPOD_SIZE_SHIFT;
12438c2ecf20Sopenharmony_ci	tagmask = cxgbi_tagmask_set(ppmax);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	pr_info("T3 %s: 0x%x~0x%x, 0x%x, tagmask 0x%x -> 0x%x.\n",
12468c2ecf20Sopenharmony_ci		ndev->name, uinfo.llimit, uinfo.ulimit, ppmax, uinfo.tagmask,
12478c2ecf20Sopenharmony_ci		tagmask);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	memset(&tformat, 0, sizeof(struct cxgbi_tag_format));
12508c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
12518c2ecf20Sopenharmony_ci		tformat.pgsz_order[i] = uinfo.pgsz_factor[i];
12528c2ecf20Sopenharmony_ci	cxgbi_tagmask_check(tagmask, &tformat);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	err = cxgbi_ddp_ppm_setup(&tdev->ulp_iscsi, cdev, &tformat,
12558c2ecf20Sopenharmony_ci				  (uinfo.ulimit - uinfo.llimit + 1),
12568c2ecf20Sopenharmony_ci				  uinfo.llimit, uinfo.llimit, 0, 0, 0);
12578c2ecf20Sopenharmony_ci	if (err)
12588c2ecf20Sopenharmony_ci		return err;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	if (!(cdev->flags & CXGBI_FLAG_DDP_OFF)) {
12618c2ecf20Sopenharmony_ci		uinfo.tagmask = tagmask;
12628c2ecf20Sopenharmony_ci		uinfo.ulimit = uinfo.llimit + (ppmax << PPOD_SIZE_SHIFT);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci		err = tdev->ctl(tdev, ULP_ISCSI_SET_PARAMS, &uinfo);
12658c2ecf20Sopenharmony_ci		if (err < 0) {
12668c2ecf20Sopenharmony_ci			pr_err("T3 %s fail to set iscsi param %d.\n",
12678c2ecf20Sopenharmony_ci			       ndev->name, err);
12688c2ecf20Sopenharmony_ci			cdev->flags |= CXGBI_FLAG_DDP_OFF;
12698c2ecf20Sopenharmony_ci		}
12708c2ecf20Sopenharmony_ci		err = 0;
12718c2ecf20Sopenharmony_ci	}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	cdev->csk_ddp_setup_digest = ddp_setup_conn_digest;
12748c2ecf20Sopenharmony_ci	cdev->csk_ddp_setup_pgidx = ddp_setup_conn_pgidx;
12758c2ecf20Sopenharmony_ci	cdev->csk_ddp_set_map = ddp_set_map;
12768c2ecf20Sopenharmony_ci	cdev->csk_ddp_clear_map = ddp_clear_map;
12778c2ecf20Sopenharmony_ci	cdev->cdev2ppm = cdev2ppm;
12788c2ecf20Sopenharmony_ci	cdev->tx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
12798c2ecf20Sopenharmony_ci				  uinfo.max_txsz - ISCSI_PDU_NONPAYLOAD_LEN);
12808c2ecf20Sopenharmony_ci	cdev->rx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
12818c2ecf20Sopenharmony_ci				  uinfo.max_rxsz - ISCSI_PDU_NONPAYLOAD_LEN);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	return 0;
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_cistatic void cxgb3i_dev_close(struct t3cdev *t3dev)
12878c2ecf20Sopenharmony_ci{
12888c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = cxgbi_device_find_by_lldev(t3dev);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	if (!cdev || cdev->flags & CXGBI_FLAG_ADAPTER_RESET) {
12918c2ecf20Sopenharmony_ci		pr_info("0x%p close, f 0x%x.\n", cdev, cdev ? cdev->flags : 0);
12928c2ecf20Sopenharmony_ci		return;
12938c2ecf20Sopenharmony_ci	}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	cxgbi_device_unregister(cdev);
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci/**
12998c2ecf20Sopenharmony_ci * cxgb3i_dev_open - init a t3 adapter structure and any h/w settings
13008c2ecf20Sopenharmony_ci * @t3dev: t3cdev adapter
13018c2ecf20Sopenharmony_ci */
13028c2ecf20Sopenharmony_cistatic void cxgb3i_dev_open(struct t3cdev *t3dev)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = cxgbi_device_find_by_lldev(t3dev);
13058c2ecf20Sopenharmony_ci	struct adapter *adapter = tdev2adap(t3dev);
13068c2ecf20Sopenharmony_ci	int i, err;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	if (cdev) {
13098c2ecf20Sopenharmony_ci		pr_info("0x%p, updating.\n", cdev);
13108c2ecf20Sopenharmony_ci		return;
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	cdev = cxgbi_device_register(0, adapter->params.nports);
13148c2ecf20Sopenharmony_ci	if (!cdev) {
13158c2ecf20Sopenharmony_ci		pr_warn("device 0x%p register failed.\n", t3dev);
13168c2ecf20Sopenharmony_ci		return;
13178c2ecf20Sopenharmony_ci	}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	cdev->flags = CXGBI_FLAG_DEV_T3 | CXGBI_FLAG_IPV4_SET;
13208c2ecf20Sopenharmony_ci	cdev->lldev = t3dev;
13218c2ecf20Sopenharmony_ci	cdev->pdev = adapter->pdev;
13228c2ecf20Sopenharmony_ci	cdev->ports = adapter->port;
13238c2ecf20Sopenharmony_ci	cdev->nports = adapter->params.nports;
13248c2ecf20Sopenharmony_ci	cdev->mtus = adapter->params.mtus;
13258c2ecf20Sopenharmony_ci	cdev->nmtus = NMTUS;
13268c2ecf20Sopenharmony_ci	cdev->rx_credit_thres = cxgb3i_rx_credit_thres;
13278c2ecf20Sopenharmony_ci	cdev->skb_tx_rsvd = CXGB3I_TX_HEADER_LEN;
13288c2ecf20Sopenharmony_ci	cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr_norss);
13298c2ecf20Sopenharmony_ci	cdev->itp = &cxgb3i_iscsi_transport;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	err = cxgb3i_ddp_init(cdev);
13328c2ecf20Sopenharmony_ci	if (err) {
13338c2ecf20Sopenharmony_ci		pr_info("0x%p ddp init failed %d\n", cdev, err);
13348c2ecf20Sopenharmony_ci		goto err_out;
13358c2ecf20Sopenharmony_ci	}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	err = cxgb3i_ofld_init(cdev);
13388c2ecf20Sopenharmony_ci	if (err) {
13398c2ecf20Sopenharmony_ci		pr_info("0x%p offload init failed\n", cdev);
13408c2ecf20Sopenharmony_ci		goto err_out;
13418c2ecf20Sopenharmony_ci	}
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	err = cxgbi_hbas_add(cdev, CXGB3I_MAX_LUN, CXGBI_MAX_CONN,
13448c2ecf20Sopenharmony_ci				&cxgb3i_host_template, cxgb3i_stt);
13458c2ecf20Sopenharmony_ci	if (err)
13468c2ecf20Sopenharmony_ci		goto err_out;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	for (i = 0; i < cdev->nports; i++)
13498c2ecf20Sopenharmony_ci		cdev->hbas[i]->ipv4addr =
13508c2ecf20Sopenharmony_ci			cxgb3i_get_private_ipv4addr(cdev->ports[i]);
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	pr_info("cdev 0x%p, f 0x%x, t3dev 0x%p open, err %d.\n",
13538c2ecf20Sopenharmony_ci		cdev, cdev ? cdev->flags : 0, t3dev, err);
13548c2ecf20Sopenharmony_ci	return;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_cierr_out:
13578c2ecf20Sopenharmony_ci	cxgbi_device_unregister(cdev);
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_cistatic void cxgb3i_dev_event_handler(struct t3cdev *t3dev, u32 event, u32 port)
13618c2ecf20Sopenharmony_ci{
13628c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = cxgbi_device_find_by_lldev(t3dev);
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE,
13658c2ecf20Sopenharmony_ci		"0x%p, cdev 0x%p, event 0x%x, port 0x%x.\n",
13668c2ecf20Sopenharmony_ci		t3dev, cdev, event, port);
13678c2ecf20Sopenharmony_ci	if (!cdev)
13688c2ecf20Sopenharmony_ci		return;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	switch (event) {
13718c2ecf20Sopenharmony_ci	case OFFLOAD_STATUS_DOWN:
13728c2ecf20Sopenharmony_ci		cdev->flags |= CXGBI_FLAG_ADAPTER_RESET;
13738c2ecf20Sopenharmony_ci		break;
13748c2ecf20Sopenharmony_ci	case OFFLOAD_STATUS_UP:
13758c2ecf20Sopenharmony_ci		cdev->flags &= ~CXGBI_FLAG_ADAPTER_RESET;
13768c2ecf20Sopenharmony_ci		break;
13778c2ecf20Sopenharmony_ci	}
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci/**
13818c2ecf20Sopenharmony_ci * cxgb3i_init_module - module init entry point
13828c2ecf20Sopenharmony_ci *
13838c2ecf20Sopenharmony_ci * initialize any driver wide global data structures and register itself
13848c2ecf20Sopenharmony_ci *	with the cxgb3 module
13858c2ecf20Sopenharmony_ci */
13868c2ecf20Sopenharmony_cistatic int __init cxgb3i_init_module(void)
13878c2ecf20Sopenharmony_ci{
13888c2ecf20Sopenharmony_ci	int rc;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s", version);
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	rc = cxgbi_iscsi_init(&cxgb3i_iscsi_transport, &cxgb3i_stt);
13938c2ecf20Sopenharmony_ci	if (rc < 0)
13948c2ecf20Sopenharmony_ci		return rc;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	cxgb3_register_client(&t3_client);
13978c2ecf20Sopenharmony_ci	return 0;
13988c2ecf20Sopenharmony_ci}
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci/**
14018c2ecf20Sopenharmony_ci * cxgb3i_exit_module - module cleanup/exit entry point
14028c2ecf20Sopenharmony_ci *
14038c2ecf20Sopenharmony_ci * go through the driver hba list and for each hba, release any resource held.
14048c2ecf20Sopenharmony_ci *	and unregisters iscsi transport and the cxgb3 module
14058c2ecf20Sopenharmony_ci */
14068c2ecf20Sopenharmony_cistatic void __exit cxgb3i_exit_module(void)
14078c2ecf20Sopenharmony_ci{
14088c2ecf20Sopenharmony_ci	cxgb3_unregister_client(&t3_client);
14098c2ecf20Sopenharmony_ci	cxgbi_device_unregister_all(CXGBI_FLAG_DEV_T3);
14108c2ecf20Sopenharmony_ci	cxgbi_iscsi_cleanup(&cxgb3i_iscsi_transport, &cxgb3i_stt);
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cimodule_init(cxgb3i_init_module);
14148c2ecf20Sopenharmony_cimodule_exit(cxgb3i_exit_module);
1415