162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2016 Chelsio Communications, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define DRV_NAME "cxgbit"
762306a36Sopenharmony_ci#define DRV_VERSION "1.0.0-ko"
862306a36Sopenharmony_ci#define pr_fmt(fmt) DRV_NAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "cxgbit.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T4_DCB
1362306a36Sopenharmony_ci#include <net/dcbevent.h>
1462306a36Sopenharmony_ci#include "cxgb4_dcb.h"
1562306a36Sopenharmony_ci#endif
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciLIST_HEAD(cdev_list_head);
1862306a36Sopenharmony_ci/* cdev list lock */
1962306a36Sopenharmony_ciDEFINE_MUTEX(cdev_list_lock);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_civoid _cxgbit_free_cdev(struct kref *kref)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct cxgbit_device *cdev;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	cdev = container_of(kref, struct cxgbit_device, kref);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	cxgbi_ppm_release(cdev2ppm(cdev));
2862306a36Sopenharmony_ci	kfree(cdev);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void cxgbit_set_mdsl(struct cxgbit_device *cdev)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct cxgb4_lld_info *lldi = &cdev->lldi;
3462306a36Sopenharmony_ci	u32 mdsl;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define CXGBIT_T5_MAX_PDU_LEN 16224
3762306a36Sopenharmony_ci#define CXGBIT_PDU_NONPAYLOAD_LEN 312 /* 48(BHS) + 256(AHS) + 8(Digest) */
3862306a36Sopenharmony_ci	if (is_t5(lldi->adapter_type)) {
3962306a36Sopenharmony_ci		mdsl = min_t(u32, lldi->iscsi_iolen - CXGBIT_PDU_NONPAYLOAD_LEN,
4062306a36Sopenharmony_ci			     CXGBIT_T5_MAX_PDU_LEN - CXGBIT_PDU_NONPAYLOAD_LEN);
4162306a36Sopenharmony_ci	} else {
4262306a36Sopenharmony_ci		mdsl = lldi->iscsi_iolen - CXGBIT_PDU_NONPAYLOAD_LEN;
4362306a36Sopenharmony_ci		mdsl = min(mdsl, 16384U);
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	mdsl = round_down(mdsl, 4);
4762306a36Sopenharmony_ci	mdsl = min_t(u32, mdsl, 4 * PAGE_SIZE);
4862306a36Sopenharmony_ci	mdsl = min_t(u32, mdsl, (MAX_SKB_FRAGS - 1) * PAGE_SIZE);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	cdev->mdsl = mdsl;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void *cxgbit_uld_add(const struct cxgb4_lld_info *lldi)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct cxgbit_device *cdev;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (is_t4(lldi->adapter_type))
5862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
6162306a36Sopenharmony_ci	if (!cdev)
6262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	kref_init(&cdev->kref);
6562306a36Sopenharmony_ci	spin_lock_init(&cdev->np_lock);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	cdev->lldi = *lldi;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	cxgbit_set_mdsl(cdev);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (cxgbit_ddp_init(cdev) < 0) {
7262306a36Sopenharmony_ci		kfree(cdev);
7362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (!test_bit(CDEV_DDP_ENABLE, &cdev->flags))
7762306a36Sopenharmony_ci		pr_info("cdev %s ddp init failed\n",
7862306a36Sopenharmony_ci			pci_name(lldi->pdev));
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (lldi->fw_vers >= 0x10d2b00)
8162306a36Sopenharmony_ci		set_bit(CDEV_ISO_ENABLE, &cdev->flags);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	spin_lock_init(&cdev->cskq.lock);
8462306a36Sopenharmony_ci	INIT_LIST_HEAD(&cdev->cskq.list);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	mutex_lock(&cdev_list_lock);
8762306a36Sopenharmony_ci	list_add_tail(&cdev->list, &cdev_list_head);
8862306a36Sopenharmony_ci	mutex_unlock(&cdev_list_lock);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	pr_info("cdev %s added for iSCSI target transport\n",
9162306a36Sopenharmony_ci		pci_name(lldi->pdev));
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return cdev;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void cxgbit_close_conn(struct cxgbit_device *cdev)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct cxgbit_sock *csk;
9962306a36Sopenharmony_ci	struct sk_buff *skb;
10062306a36Sopenharmony_ci	bool wakeup_thread = false;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	spin_lock_bh(&cdev->cskq.lock);
10362306a36Sopenharmony_ci	list_for_each_entry(csk, &cdev->cskq.list, list) {
10462306a36Sopenharmony_ci		skb = alloc_skb(0, GFP_ATOMIC);
10562306a36Sopenharmony_ci		if (!skb)
10662306a36Sopenharmony_ci			continue;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		spin_lock_bh(&csk->rxq.lock);
10962306a36Sopenharmony_ci		__skb_queue_tail(&csk->rxq, skb);
11062306a36Sopenharmony_ci		if (skb_queue_len(&csk->rxq) == 1)
11162306a36Sopenharmony_ci			wakeup_thread = true;
11262306a36Sopenharmony_ci		spin_unlock_bh(&csk->rxq.lock);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (wakeup_thread) {
11562306a36Sopenharmony_ci			wake_up(&csk->waitq);
11662306a36Sopenharmony_ci			wakeup_thread = false;
11762306a36Sopenharmony_ci		}
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	spin_unlock_bh(&cdev->cskq.lock);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void cxgbit_detach_cdev(struct cxgbit_device *cdev)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	bool free_cdev = false;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	spin_lock_bh(&cdev->cskq.lock);
12762306a36Sopenharmony_ci	if (list_empty(&cdev->cskq.list))
12862306a36Sopenharmony_ci		free_cdev = true;
12962306a36Sopenharmony_ci	spin_unlock_bh(&cdev->cskq.lock);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (free_cdev) {
13262306a36Sopenharmony_ci		mutex_lock(&cdev_list_lock);
13362306a36Sopenharmony_ci		list_del(&cdev->list);
13462306a36Sopenharmony_ci		mutex_unlock(&cdev_list_lock);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		cxgbit_put_cdev(cdev);
13762306a36Sopenharmony_ci	} else {
13862306a36Sopenharmony_ci		cxgbit_close_conn(cdev);
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int cxgbit_uld_state_change(void *handle, enum cxgb4_state state)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct cxgbit_device *cdev = handle;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	switch (state) {
14762306a36Sopenharmony_ci	case CXGB4_STATE_UP:
14862306a36Sopenharmony_ci		set_bit(CDEV_STATE_UP, &cdev->flags);
14962306a36Sopenharmony_ci		pr_info("cdev %s state UP.\n", pci_name(cdev->lldi.pdev));
15062306a36Sopenharmony_ci		break;
15162306a36Sopenharmony_ci	case CXGB4_STATE_START_RECOVERY:
15262306a36Sopenharmony_ci		clear_bit(CDEV_STATE_UP, &cdev->flags);
15362306a36Sopenharmony_ci		cxgbit_close_conn(cdev);
15462306a36Sopenharmony_ci		pr_info("cdev %s state RECOVERY.\n", pci_name(cdev->lldi.pdev));
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	case CXGB4_STATE_DOWN:
15762306a36Sopenharmony_ci		pr_info("cdev %s state DOWN.\n", pci_name(cdev->lldi.pdev));
15862306a36Sopenharmony_ci		break;
15962306a36Sopenharmony_ci	case CXGB4_STATE_DETACH:
16062306a36Sopenharmony_ci		clear_bit(CDEV_STATE_UP, &cdev->flags);
16162306a36Sopenharmony_ci		pr_info("cdev %s state DETACH.\n", pci_name(cdev->lldi.pdev));
16262306a36Sopenharmony_ci		cxgbit_detach_cdev(cdev);
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	default:
16562306a36Sopenharmony_ci		pr_info("cdev %s unknown state %d.\n",
16662306a36Sopenharmony_ci			pci_name(cdev->lldi.pdev), state);
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void
17362306a36Sopenharmony_cicxgbit_process_ddpvld(struct cxgbit_sock *csk, struct cxgbit_lro_pdu_cb *pdu_cb,
17462306a36Sopenharmony_ci		      u32 ddpvld)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (ddpvld & (1 << CPL_RX_ISCSI_DDP_STATUS_HCRC_SHIFT)) {
17862306a36Sopenharmony_ci		pr_info("tid 0x%x, status 0x%x, hcrc bad.\n", csk->tid, ddpvld);
17962306a36Sopenharmony_ci		pdu_cb->flags |= PDUCBF_RX_HCRC_ERR;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (ddpvld & (1 << CPL_RX_ISCSI_DDP_STATUS_DCRC_SHIFT)) {
18362306a36Sopenharmony_ci		pr_info("tid 0x%x, status 0x%x, dcrc bad.\n", csk->tid, ddpvld);
18462306a36Sopenharmony_ci		pdu_cb->flags |= PDUCBF_RX_DCRC_ERR;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (ddpvld & (1 << CPL_RX_ISCSI_DDP_STATUS_PAD_SHIFT))
18862306a36Sopenharmony_ci		pr_info("tid 0x%x, status 0x%x, pad bad.\n", csk->tid, ddpvld);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if ((ddpvld & (1 << CPL_RX_ISCSI_DDP_STATUS_DDP_SHIFT)) &&
19162306a36Sopenharmony_ci	    (!(pdu_cb->flags & PDUCBF_RX_DATA))) {
19262306a36Sopenharmony_ci		pdu_cb->flags |= PDUCBF_RX_DATA_DDPD;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void
19762306a36Sopenharmony_cicxgbit_lro_add_packet_rsp(struct sk_buff *skb, u8 op, const __be64 *rsp)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct cxgbit_lro_cb *lro_cb = cxgbit_skb_lro_cb(skb);
20062306a36Sopenharmony_ci	struct cxgbit_lro_pdu_cb *pdu_cb = cxgbit_skb_lro_pdu_cb(skb,
20162306a36Sopenharmony_ci						lro_cb->pdu_idx);
20262306a36Sopenharmony_ci	struct cpl_rx_iscsi_ddp *cpl = (struct cpl_rx_iscsi_ddp *)(rsp + 1);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	cxgbit_process_ddpvld(lro_cb->csk, pdu_cb, be32_to_cpu(cpl->ddpvld));
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	pdu_cb->flags |= PDUCBF_RX_STATUS;
20762306a36Sopenharmony_ci	pdu_cb->ddigest = ntohl(cpl->ulp_crc);
20862306a36Sopenharmony_ci	pdu_cb->pdulen = ntohs(cpl->len);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (pdu_cb->flags & PDUCBF_RX_HDR)
21162306a36Sopenharmony_ci		pdu_cb->complete = true;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	lro_cb->pdu_totallen += pdu_cb->pdulen;
21462306a36Sopenharmony_ci	lro_cb->complete = true;
21562306a36Sopenharmony_ci	lro_cb->pdu_idx++;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void
21962306a36Sopenharmony_cicxgbit_copy_frags(struct sk_buff *skb, const struct pkt_gl *gl,
22062306a36Sopenharmony_ci		  unsigned int offset)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	u8 skb_frag_idx = skb_shinfo(skb)->nr_frags;
22362306a36Sopenharmony_ci	u8 i;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* usually there's just one frag */
22662306a36Sopenharmony_ci	__skb_fill_page_desc(skb, skb_frag_idx, gl->frags[0].page,
22762306a36Sopenharmony_ci			     gl->frags[0].offset + offset,
22862306a36Sopenharmony_ci			     gl->frags[0].size - offset);
22962306a36Sopenharmony_ci	for (i = 1; i < gl->nfrags; i++)
23062306a36Sopenharmony_ci		__skb_fill_page_desc(skb, skb_frag_idx + i,
23162306a36Sopenharmony_ci				     gl->frags[i].page,
23262306a36Sopenharmony_ci				     gl->frags[i].offset,
23362306a36Sopenharmony_ci				     gl->frags[i].size);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	skb_shinfo(skb)->nr_frags += gl->nfrags;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* get a reference to the last page, we don't own it */
23862306a36Sopenharmony_ci	get_page(gl->frags[gl->nfrags - 1].page);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void
24262306a36Sopenharmony_cicxgbit_lro_add_packet_gl(struct sk_buff *skb, u8 op, const struct pkt_gl *gl)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct cxgbit_lro_cb *lro_cb = cxgbit_skb_lro_cb(skb);
24562306a36Sopenharmony_ci	struct cxgbit_lro_pdu_cb *pdu_cb = cxgbit_skb_lro_pdu_cb(skb,
24662306a36Sopenharmony_ci						lro_cb->pdu_idx);
24762306a36Sopenharmony_ci	u32 len, offset;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (op == CPL_ISCSI_HDR) {
25062306a36Sopenharmony_ci		struct cpl_iscsi_hdr *cpl = (struct cpl_iscsi_hdr *)gl->va;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		offset = sizeof(struct cpl_iscsi_hdr);
25362306a36Sopenharmony_ci		pdu_cb->flags |= PDUCBF_RX_HDR;
25462306a36Sopenharmony_ci		pdu_cb->seq = ntohl(cpl->seq);
25562306a36Sopenharmony_ci		len = ntohs(cpl->len);
25662306a36Sopenharmony_ci		pdu_cb->hdr = gl->va + offset;
25762306a36Sopenharmony_ci		pdu_cb->hlen = len;
25862306a36Sopenharmony_ci		pdu_cb->hfrag_idx = skb_shinfo(skb)->nr_frags;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		if (unlikely(gl->nfrags > 1))
26162306a36Sopenharmony_ci			cxgbit_skcb_flags(skb) = 0;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		lro_cb->complete = false;
26462306a36Sopenharmony_ci	} else if (op == CPL_ISCSI_DATA) {
26562306a36Sopenharmony_ci		struct cpl_iscsi_data *cpl = (struct cpl_iscsi_data *)gl->va;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		offset = sizeof(struct cpl_iscsi_data);
26862306a36Sopenharmony_ci		pdu_cb->flags |= PDUCBF_RX_DATA;
26962306a36Sopenharmony_ci		len = ntohs(cpl->len);
27062306a36Sopenharmony_ci		pdu_cb->dlen = len;
27162306a36Sopenharmony_ci		pdu_cb->doffset = lro_cb->offset;
27262306a36Sopenharmony_ci		pdu_cb->nr_dfrags = gl->nfrags;
27362306a36Sopenharmony_ci		pdu_cb->dfrag_idx = skb_shinfo(skb)->nr_frags;
27462306a36Sopenharmony_ci		lro_cb->complete = false;
27562306a36Sopenharmony_ci	} else {
27662306a36Sopenharmony_ci		struct cpl_rx_iscsi_cmp *cpl;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		cpl = (struct cpl_rx_iscsi_cmp *)gl->va;
27962306a36Sopenharmony_ci		offset = sizeof(struct cpl_rx_iscsi_cmp);
28062306a36Sopenharmony_ci		pdu_cb->flags |= (PDUCBF_RX_HDR | PDUCBF_RX_STATUS);
28162306a36Sopenharmony_ci		len = be16_to_cpu(cpl->len);
28262306a36Sopenharmony_ci		pdu_cb->hdr = gl->va + offset;
28362306a36Sopenharmony_ci		pdu_cb->hlen = len;
28462306a36Sopenharmony_ci		pdu_cb->hfrag_idx = skb_shinfo(skb)->nr_frags;
28562306a36Sopenharmony_ci		pdu_cb->ddigest = be32_to_cpu(cpl->ulp_crc);
28662306a36Sopenharmony_ci		pdu_cb->pdulen = ntohs(cpl->len);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		if (unlikely(gl->nfrags > 1))
28962306a36Sopenharmony_ci			cxgbit_skcb_flags(skb) = 0;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		cxgbit_process_ddpvld(lro_cb->csk, pdu_cb,
29262306a36Sopenharmony_ci				      be32_to_cpu(cpl->ddpvld));
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (pdu_cb->flags & PDUCBF_RX_DATA_DDPD) {
29562306a36Sopenharmony_ci			pdu_cb->flags |= PDUCBF_RX_DDP_CMP;
29662306a36Sopenharmony_ci			pdu_cb->complete = true;
29762306a36Sopenharmony_ci		} else if (pdu_cb->flags & PDUCBF_RX_DATA) {
29862306a36Sopenharmony_ci			pdu_cb->complete = true;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		lro_cb->pdu_totallen += pdu_cb->hlen + pdu_cb->dlen;
30262306a36Sopenharmony_ci		lro_cb->complete = true;
30362306a36Sopenharmony_ci		lro_cb->pdu_idx++;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	cxgbit_copy_frags(skb, gl, offset);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	pdu_cb->frags += gl->nfrags;
30962306a36Sopenharmony_ci	lro_cb->offset += len;
31062306a36Sopenharmony_ci	skb->len += len;
31162306a36Sopenharmony_ci	skb->data_len += len;
31262306a36Sopenharmony_ci	skb->truesize += len;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct sk_buff *
31662306a36Sopenharmony_cicxgbit_lro_init_skb(struct cxgbit_sock *csk, u8 op, const struct pkt_gl *gl,
31762306a36Sopenharmony_ci		    const __be64 *rsp, struct napi_struct *napi)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct sk_buff *skb;
32062306a36Sopenharmony_ci	struct cxgbit_lro_cb *lro_cb;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	skb = napi_alloc_skb(napi, LRO_SKB_MAX_HEADROOM);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (unlikely(!skb))
32562306a36Sopenharmony_ci		return NULL;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	memset(skb->data, 0, LRO_SKB_MAX_HEADROOM);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	cxgbit_skcb_flags(skb) |= SKCBF_RX_LRO;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	lro_cb = cxgbit_skb_lro_cb(skb);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	cxgbit_get_csk(csk);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	lro_cb->csk = csk;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return skb;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic void cxgbit_queue_lro_skb(struct cxgbit_sock *csk, struct sk_buff *skb)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	bool wakeup_thread = false;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	spin_lock(&csk->rxq.lock);
34562306a36Sopenharmony_ci	__skb_queue_tail(&csk->rxq, skb);
34662306a36Sopenharmony_ci	if (skb_queue_len(&csk->rxq) == 1)
34762306a36Sopenharmony_ci		wakeup_thread = true;
34862306a36Sopenharmony_ci	spin_unlock(&csk->rxq.lock);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (wakeup_thread)
35162306a36Sopenharmony_ci		wake_up(&csk->waitq);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void cxgbit_lro_flush(struct t4_lro_mgr *lro_mgr, struct sk_buff *skb)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct cxgbit_lro_cb *lro_cb = cxgbit_skb_lro_cb(skb);
35762306a36Sopenharmony_ci	struct cxgbit_sock *csk = lro_cb->csk;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	csk->lro_skb = NULL;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	__skb_unlink(skb, &lro_mgr->lroq);
36262306a36Sopenharmony_ci	cxgbit_queue_lro_skb(csk, skb);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	cxgbit_put_csk(csk);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	lro_mgr->lro_pkts++;
36762306a36Sopenharmony_ci	lro_mgr->lro_session_cnt--;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void cxgbit_uld_lro_flush(struct t4_lro_mgr *lro_mgr)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct sk_buff *skb;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	while ((skb = skb_peek(&lro_mgr->lroq)))
37562306a36Sopenharmony_ci		cxgbit_lro_flush(lro_mgr, skb);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic int
37962306a36Sopenharmony_cicxgbit_lro_receive(struct cxgbit_sock *csk, u8 op, const __be64 *rsp,
38062306a36Sopenharmony_ci		   const struct pkt_gl *gl, struct t4_lro_mgr *lro_mgr,
38162306a36Sopenharmony_ci		   struct napi_struct *napi)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct sk_buff *skb;
38462306a36Sopenharmony_ci	struct cxgbit_lro_cb *lro_cb;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (!csk) {
38762306a36Sopenharmony_ci		pr_err("%s: csk NULL, op 0x%x.\n", __func__, op);
38862306a36Sopenharmony_ci		goto out;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (csk->lro_skb)
39262306a36Sopenharmony_ci		goto add_packet;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistart_lro:
39562306a36Sopenharmony_ci	if (lro_mgr->lro_session_cnt >= MAX_LRO_SESSIONS) {
39662306a36Sopenharmony_ci		cxgbit_uld_lro_flush(lro_mgr);
39762306a36Sopenharmony_ci		goto start_lro;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	skb = cxgbit_lro_init_skb(csk, op, gl, rsp, napi);
40162306a36Sopenharmony_ci	if (unlikely(!skb))
40262306a36Sopenharmony_ci		goto out;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	csk->lro_skb = skb;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	__skb_queue_tail(&lro_mgr->lroq, skb);
40762306a36Sopenharmony_ci	lro_mgr->lro_session_cnt++;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ciadd_packet:
41062306a36Sopenharmony_ci	skb = csk->lro_skb;
41162306a36Sopenharmony_ci	lro_cb = cxgbit_skb_lro_cb(skb);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if ((gl && (((skb_shinfo(skb)->nr_frags + gl->nfrags) >
41462306a36Sopenharmony_ci	    MAX_SKB_FRAGS) || (lro_cb->pdu_totallen >= LRO_FLUSH_LEN_MAX))) ||
41562306a36Sopenharmony_ci	    (lro_cb->pdu_idx >= MAX_SKB_FRAGS)) {
41662306a36Sopenharmony_ci		cxgbit_lro_flush(lro_mgr, skb);
41762306a36Sopenharmony_ci		goto start_lro;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (gl)
42162306a36Sopenharmony_ci		cxgbit_lro_add_packet_gl(skb, op, gl);
42262306a36Sopenharmony_ci	else
42362306a36Sopenharmony_ci		cxgbit_lro_add_packet_rsp(skb, op, rsp);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	lro_mgr->lro_merged++;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return 0;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ciout:
43062306a36Sopenharmony_ci	return -1;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int
43462306a36Sopenharmony_cicxgbit_uld_lro_rx_handler(void *hndl, const __be64 *rsp,
43562306a36Sopenharmony_ci			  const struct pkt_gl *gl, struct t4_lro_mgr *lro_mgr,
43662306a36Sopenharmony_ci			  struct napi_struct *napi)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct cxgbit_device *cdev = hndl;
43962306a36Sopenharmony_ci	struct cxgb4_lld_info *lldi = &cdev->lldi;
44062306a36Sopenharmony_ci	struct cpl_tx_data *rpl = NULL;
44162306a36Sopenharmony_ci	struct cxgbit_sock *csk = NULL;
44262306a36Sopenharmony_ci	unsigned int tid = 0;
44362306a36Sopenharmony_ci	struct sk_buff *skb;
44462306a36Sopenharmony_ci	unsigned int op = *(u8 *)rsp;
44562306a36Sopenharmony_ci	bool lro_flush = true;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	switch (op) {
44862306a36Sopenharmony_ci	case CPL_ISCSI_HDR:
44962306a36Sopenharmony_ci	case CPL_ISCSI_DATA:
45062306a36Sopenharmony_ci	case CPL_RX_ISCSI_CMP:
45162306a36Sopenharmony_ci	case CPL_RX_ISCSI_DDP:
45262306a36Sopenharmony_ci	case CPL_FW4_ACK:
45362306a36Sopenharmony_ci		lro_flush = false;
45462306a36Sopenharmony_ci		fallthrough;
45562306a36Sopenharmony_ci	case CPL_ABORT_RPL_RSS:
45662306a36Sopenharmony_ci	case CPL_PASS_ESTABLISH:
45762306a36Sopenharmony_ci	case CPL_PEER_CLOSE:
45862306a36Sopenharmony_ci	case CPL_CLOSE_CON_RPL:
45962306a36Sopenharmony_ci	case CPL_ABORT_REQ_RSS:
46062306a36Sopenharmony_ci	case CPL_SET_TCB_RPL:
46162306a36Sopenharmony_ci	case CPL_RX_DATA:
46262306a36Sopenharmony_ci		rpl = gl ? (struct cpl_tx_data *)gl->va :
46362306a36Sopenharmony_ci			   (struct cpl_tx_data *)(rsp + 1);
46462306a36Sopenharmony_ci		tid = GET_TID(rpl);
46562306a36Sopenharmony_ci		csk = lookup_tid(lldi->tids, tid);
46662306a36Sopenharmony_ci		break;
46762306a36Sopenharmony_ci	default:
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (csk && csk->lro_skb && lro_flush)
47262306a36Sopenharmony_ci		cxgbit_lro_flush(lro_mgr, csk->lro_skb);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (!gl) {
47562306a36Sopenharmony_ci		unsigned int len;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		if (op == CPL_RX_ISCSI_DDP) {
47862306a36Sopenharmony_ci			if (!cxgbit_lro_receive(csk, op, rsp, NULL, lro_mgr,
47962306a36Sopenharmony_ci						napi))
48062306a36Sopenharmony_ci				return 0;
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		len = 64 - sizeof(struct rsp_ctrl) - 8;
48462306a36Sopenharmony_ci		skb = napi_alloc_skb(napi, len);
48562306a36Sopenharmony_ci		if (!skb)
48662306a36Sopenharmony_ci			goto nomem;
48762306a36Sopenharmony_ci		__skb_put(skb, len);
48862306a36Sopenharmony_ci		skb_copy_to_linear_data(skb, &rsp[1], len);
48962306a36Sopenharmony_ci	} else {
49062306a36Sopenharmony_ci		if (unlikely(op != *(u8 *)gl->va)) {
49162306a36Sopenharmony_ci			pr_info("? FL 0x%p,RSS%#llx,FL %#llx,len %u.\n",
49262306a36Sopenharmony_ci				gl->va, be64_to_cpu(*rsp),
49362306a36Sopenharmony_ci				get_unaligned_be64(gl->va),
49462306a36Sopenharmony_ci				gl->tot_len);
49562306a36Sopenharmony_ci			return 0;
49662306a36Sopenharmony_ci		}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		if ((op == CPL_ISCSI_HDR) || (op == CPL_ISCSI_DATA) ||
49962306a36Sopenharmony_ci		    (op == CPL_RX_ISCSI_CMP)) {
50062306a36Sopenharmony_ci			if (!cxgbit_lro_receive(csk, op, rsp, gl, lro_mgr,
50162306a36Sopenharmony_ci						napi))
50262306a36Sopenharmony_ci				return 0;
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci#define RX_PULL_LEN 128
50662306a36Sopenharmony_ci		skb = cxgb4_pktgl_to_skb(gl, RX_PULL_LEN, RX_PULL_LEN);
50762306a36Sopenharmony_ci		if (unlikely(!skb))
50862306a36Sopenharmony_ci			goto nomem;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	rpl = (struct cpl_tx_data *)skb->data;
51262306a36Sopenharmony_ci	op = rpl->ot.opcode;
51362306a36Sopenharmony_ci	cxgbit_skcb_rx_opcode(skb) = op;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	pr_debug("cdev %p, opcode 0x%x(0x%x,0x%x), skb %p.\n",
51662306a36Sopenharmony_ci		 cdev, op, rpl->ot.opcode_tid,
51762306a36Sopenharmony_ci		 ntohl(rpl->ot.opcode_tid), skb);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (op < NUM_CPL_CMDS && cxgbit_cplhandlers[op]) {
52062306a36Sopenharmony_ci		cxgbit_cplhandlers[op](cdev, skb);
52162306a36Sopenharmony_ci	} else {
52262306a36Sopenharmony_ci		pr_err("No handler for opcode 0x%x.\n", op);
52362306a36Sopenharmony_ci		__kfree_skb(skb);
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_cinomem:
52762306a36Sopenharmony_ci	pr_err("%s OOM bailing out.\n", __func__);
52862306a36Sopenharmony_ci	return 1;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T4_DCB
53262306a36Sopenharmony_cistruct cxgbit_dcb_work {
53362306a36Sopenharmony_ci	struct dcb_app_type dcb_app;
53462306a36Sopenharmony_ci	struct work_struct work;
53562306a36Sopenharmony_ci};
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic void
53862306a36Sopenharmony_cicxgbit_update_dcb_priority(struct cxgbit_device *cdev, u8 port_id,
53962306a36Sopenharmony_ci			   u8 dcb_priority, u16 port_num)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct cxgbit_sock *csk;
54262306a36Sopenharmony_ci	struct sk_buff *skb;
54362306a36Sopenharmony_ci	u16 local_port;
54462306a36Sopenharmony_ci	bool wakeup_thread = false;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	spin_lock_bh(&cdev->cskq.lock);
54762306a36Sopenharmony_ci	list_for_each_entry(csk, &cdev->cskq.list, list) {
54862306a36Sopenharmony_ci		if (csk->port_id != port_id)
54962306a36Sopenharmony_ci			continue;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		if (csk->com.local_addr.ss_family == AF_INET6) {
55262306a36Sopenharmony_ci			struct sockaddr_in6 *sock_in6;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci			sock_in6 = (struct sockaddr_in6 *)&csk->com.local_addr;
55562306a36Sopenharmony_ci			local_port = ntohs(sock_in6->sin6_port);
55662306a36Sopenharmony_ci		} else {
55762306a36Sopenharmony_ci			struct sockaddr_in *sock_in;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci			sock_in = (struct sockaddr_in *)&csk->com.local_addr;
56062306a36Sopenharmony_ci			local_port = ntohs(sock_in->sin_port);
56162306a36Sopenharmony_ci		}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		if (local_port != port_num)
56462306a36Sopenharmony_ci			continue;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		if (csk->dcb_priority == dcb_priority)
56762306a36Sopenharmony_ci			continue;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		skb = alloc_skb(0, GFP_ATOMIC);
57062306a36Sopenharmony_ci		if (!skb)
57162306a36Sopenharmony_ci			continue;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		spin_lock(&csk->rxq.lock);
57462306a36Sopenharmony_ci		__skb_queue_tail(&csk->rxq, skb);
57562306a36Sopenharmony_ci		if (skb_queue_len(&csk->rxq) == 1)
57662306a36Sopenharmony_ci			wakeup_thread = true;
57762306a36Sopenharmony_ci		spin_unlock(&csk->rxq.lock);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci		if (wakeup_thread) {
58062306a36Sopenharmony_ci			wake_up(&csk->waitq);
58162306a36Sopenharmony_ci			wakeup_thread = false;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci	spin_unlock_bh(&cdev->cskq.lock);
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic void cxgbit_dcb_workfn(struct work_struct *work)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct cxgbit_dcb_work *dcb_work;
59062306a36Sopenharmony_ci	struct net_device *ndev;
59162306a36Sopenharmony_ci	struct cxgbit_device *cdev = NULL;
59262306a36Sopenharmony_ci	struct dcb_app_type *iscsi_app;
59362306a36Sopenharmony_ci	u8 priority, port_id = 0xff;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	dcb_work = container_of(work, struct cxgbit_dcb_work, work);
59662306a36Sopenharmony_ci	iscsi_app = &dcb_work->dcb_app;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (iscsi_app->dcbx & DCB_CAP_DCBX_VER_IEEE) {
59962306a36Sopenharmony_ci		if ((iscsi_app->app.selector != IEEE_8021QAZ_APP_SEL_STREAM) &&
60062306a36Sopenharmony_ci		    (iscsi_app->app.selector != IEEE_8021QAZ_APP_SEL_ANY))
60162306a36Sopenharmony_ci			goto out;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		priority = iscsi_app->app.priority;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	} else if (iscsi_app->dcbx & DCB_CAP_DCBX_VER_CEE) {
60662306a36Sopenharmony_ci		if (iscsi_app->app.selector != DCB_APP_IDTYPE_PORTNUM)
60762306a36Sopenharmony_ci			goto out;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		if (!iscsi_app->app.priority)
61062306a36Sopenharmony_ci			goto out;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		priority = ffs(iscsi_app->app.priority) - 1;
61362306a36Sopenharmony_ci	} else {
61462306a36Sopenharmony_ci		goto out;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	pr_debug("priority for ifid %d is %u\n",
61862306a36Sopenharmony_ci		 iscsi_app->ifindex, priority);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	ndev = dev_get_by_index(&init_net, iscsi_app->ifindex);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (!ndev)
62362306a36Sopenharmony_ci		goto out;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	mutex_lock(&cdev_list_lock);
62662306a36Sopenharmony_ci	cdev = cxgbit_find_device(ndev, &port_id);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	dev_put(ndev);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (!cdev) {
63162306a36Sopenharmony_ci		mutex_unlock(&cdev_list_lock);
63262306a36Sopenharmony_ci		goto out;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	cxgbit_update_dcb_priority(cdev, port_id, priority,
63662306a36Sopenharmony_ci				   iscsi_app->app.protocol);
63762306a36Sopenharmony_ci	mutex_unlock(&cdev_list_lock);
63862306a36Sopenharmony_ciout:
63962306a36Sopenharmony_ci	kfree(dcb_work);
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int
64362306a36Sopenharmony_cicxgbit_dcbevent_notify(struct notifier_block *nb, unsigned long action,
64462306a36Sopenharmony_ci		       void *data)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct cxgbit_dcb_work *dcb_work;
64762306a36Sopenharmony_ci	struct dcb_app_type *dcb_app = data;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	dcb_work = kzalloc(sizeof(*dcb_work), GFP_ATOMIC);
65062306a36Sopenharmony_ci	if (!dcb_work)
65162306a36Sopenharmony_ci		return NOTIFY_DONE;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	dcb_work->dcb_app = *dcb_app;
65462306a36Sopenharmony_ci	INIT_WORK(&dcb_work->work, cxgbit_dcb_workfn);
65562306a36Sopenharmony_ci	schedule_work(&dcb_work->work);
65662306a36Sopenharmony_ci	return NOTIFY_OK;
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci#endif
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic enum target_prot_op cxgbit_get_sup_prot_ops(struct iscsit_conn *conn)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	return TARGET_PROT_NORMAL;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic struct iscsit_transport cxgbit_transport = {
66662306a36Sopenharmony_ci	.name			= DRV_NAME,
66762306a36Sopenharmony_ci	.transport_type		= ISCSI_CXGBIT,
66862306a36Sopenharmony_ci	.rdma_shutdown		= false,
66962306a36Sopenharmony_ci	.priv_size		= sizeof(struct cxgbit_cmd),
67062306a36Sopenharmony_ci	.owner			= THIS_MODULE,
67162306a36Sopenharmony_ci	.iscsit_setup_np	= cxgbit_setup_np,
67262306a36Sopenharmony_ci	.iscsit_accept_np	= cxgbit_accept_np,
67362306a36Sopenharmony_ci	.iscsit_free_np		= cxgbit_free_np,
67462306a36Sopenharmony_ci	.iscsit_free_conn	= cxgbit_free_conn,
67562306a36Sopenharmony_ci	.iscsit_get_login_rx	= cxgbit_get_login_rx,
67662306a36Sopenharmony_ci	.iscsit_put_login_tx	= cxgbit_put_login_tx,
67762306a36Sopenharmony_ci	.iscsit_immediate_queue	= iscsit_immediate_queue,
67862306a36Sopenharmony_ci	.iscsit_response_queue	= iscsit_response_queue,
67962306a36Sopenharmony_ci	.iscsit_get_dataout	= iscsit_build_r2ts_for_cmd,
68062306a36Sopenharmony_ci	.iscsit_queue_data_in	= iscsit_queue_rsp,
68162306a36Sopenharmony_ci	.iscsit_queue_status	= iscsit_queue_rsp,
68262306a36Sopenharmony_ci	.iscsit_xmit_pdu	= cxgbit_xmit_pdu,
68362306a36Sopenharmony_ci	.iscsit_get_r2t_ttt	= cxgbit_get_r2t_ttt,
68462306a36Sopenharmony_ci	.iscsit_get_rx_pdu	= cxgbit_get_rx_pdu,
68562306a36Sopenharmony_ci	.iscsit_validate_params	= cxgbit_validate_params,
68662306a36Sopenharmony_ci	.iscsit_unmap_cmd	= cxgbit_unmap_cmd,
68762306a36Sopenharmony_ci	.iscsit_aborted_task	= iscsit_aborted_task,
68862306a36Sopenharmony_ci	.iscsit_get_sup_prot_ops = cxgbit_get_sup_prot_ops,
68962306a36Sopenharmony_ci};
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic struct cxgb4_uld_info cxgbit_uld_info = {
69262306a36Sopenharmony_ci	.name		= DRV_NAME,
69362306a36Sopenharmony_ci	.nrxq		= MAX_ULD_QSETS,
69462306a36Sopenharmony_ci	.ntxq		= MAX_ULD_QSETS,
69562306a36Sopenharmony_ci	.rxq_size	= 1024,
69662306a36Sopenharmony_ci	.lro		= true,
69762306a36Sopenharmony_ci	.add		= cxgbit_uld_add,
69862306a36Sopenharmony_ci	.state_change	= cxgbit_uld_state_change,
69962306a36Sopenharmony_ci	.lro_rx_handler = cxgbit_uld_lro_rx_handler,
70062306a36Sopenharmony_ci	.lro_flush	= cxgbit_uld_lro_flush,
70162306a36Sopenharmony_ci};
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T4_DCB
70462306a36Sopenharmony_cistatic struct notifier_block cxgbit_dcbevent_nb = {
70562306a36Sopenharmony_ci	.notifier_call = cxgbit_dcbevent_notify,
70662306a36Sopenharmony_ci};
70762306a36Sopenharmony_ci#endif
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic int __init cxgbit_init(void)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	cxgb4_register_uld(CXGB4_ULD_ISCSIT, &cxgbit_uld_info);
71262306a36Sopenharmony_ci	iscsit_register_transport(&cxgbit_transport);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T4_DCB
71562306a36Sopenharmony_ci	pr_info("%s dcb enabled.\n", DRV_NAME);
71662306a36Sopenharmony_ci	register_dcbevent_notifier(&cxgbit_dcbevent_nb);
71762306a36Sopenharmony_ci#endif
71862306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof_field(struct sk_buff, cb) <
71962306a36Sopenharmony_ci		     sizeof(union cxgbit_skb_cb));
72062306a36Sopenharmony_ci	return 0;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic void __exit cxgbit_exit(void)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct cxgbit_device *cdev, *tmp;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci#ifdef CONFIG_CHELSIO_T4_DCB
72862306a36Sopenharmony_ci	unregister_dcbevent_notifier(&cxgbit_dcbevent_nb);
72962306a36Sopenharmony_ci#endif
73062306a36Sopenharmony_ci	mutex_lock(&cdev_list_lock);
73162306a36Sopenharmony_ci	list_for_each_entry_safe(cdev, tmp, &cdev_list_head, list) {
73262306a36Sopenharmony_ci		list_del(&cdev->list);
73362306a36Sopenharmony_ci		cxgbit_put_cdev(cdev);
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci	mutex_unlock(&cdev_list_lock);
73662306a36Sopenharmony_ci	iscsit_unregister_transport(&cxgbit_transport);
73762306a36Sopenharmony_ci	cxgb4_unregister_uld(CXGB4_ULD_ISCSIT);
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cimodule_init(cxgbit_init);
74162306a36Sopenharmony_cimodule_exit(cxgbit_exit);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ciMODULE_DESCRIPTION("Chelsio iSCSI target offload driver");
74462306a36Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications");
74562306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
74662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
747