18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * libcxgbi.c: Chelsio common library for T3/T4 iSCSI driver.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2010-2015 Chelsio Communications, Inc.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
78c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
88c2ecf20Sopenharmony_ci * the Free Software Foundation.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Written by: Karen Xie (kxie@chelsio.com)
118c2ecf20Sopenharmony_ci * Written by: Rakesh Ranjan (rranjan@chelsio.com)
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ":%s: " fmt, __func__
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
178c2ecf20Sopenharmony_ci#include <linux/crypto.h>
188c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
218c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
228c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
238c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
248c2ecf20Sopenharmony_ci#include <linux/inet.h>
258c2ecf20Sopenharmony_ci#include <net/dst.h>
268c2ecf20Sopenharmony_ci#include <net/route.h>
278c2ecf20Sopenharmony_ci#include <net/ipv6.h>
288c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
298c2ecf20Sopenharmony_ci#include <net/addrconf.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>	/* ip_dev_find */
328c2ecf20Sopenharmony_ci#include <linux/module.h>
338c2ecf20Sopenharmony_ci#include <net/tcp.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic unsigned int dbg_level;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "libcxgbi.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define DRV_MODULE_NAME		"libcxgbi"
408c2ecf20Sopenharmony_ci#define DRV_MODULE_DESC		"Chelsio iSCSI driver library"
418c2ecf20Sopenharmony_ci#define DRV_MODULE_VERSION	"0.9.1-ko"
428c2ecf20Sopenharmony_ci#define DRV_MODULE_RELDATE	"Apr. 2015"
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic char version[] =
458c2ecf20Sopenharmony_ci	DRV_MODULE_DESC " " DRV_MODULE_NAME
468c2ecf20Sopenharmony_ci	" v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications, Inc.");
498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_MODULE_DESC);
508c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION);
518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cimodule_param(dbg_level, uint, 0644);
548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dbg_level, "libiscsi debug level (default=0)");
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/*
588c2ecf20Sopenharmony_ci * cxgbi device management
598c2ecf20Sopenharmony_ci * maintains a list of the cxgbi devices
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic LIST_HEAD(cdev_list);
628c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(cdev_mutex);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic LIST_HEAD(cdev_rcu_list);
658c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cdev_rcu_lock);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic inline void cxgbi_decode_sw_tag(u32 sw_tag, int *idx, int *age)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	if (age)
708c2ecf20Sopenharmony_ci		*age = sw_tag & 0x7FFF;
718c2ecf20Sopenharmony_ci	if (idx)
728c2ecf20Sopenharmony_ci		*idx = (sw_tag >> 16) & 0x7FFF;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ciint cxgbi_device_portmap_create(struct cxgbi_device *cdev, unsigned int base,
768c2ecf20Sopenharmony_ci				unsigned int max_conn)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct cxgbi_ports_map *pmap = &cdev->pmap;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	pmap->port_csk = kvzalloc(array_size(max_conn,
818c2ecf20Sopenharmony_ci					     sizeof(struct cxgbi_sock *)),
828c2ecf20Sopenharmony_ci				  GFP_KERNEL | __GFP_NOWARN);
838c2ecf20Sopenharmony_ci	if (!pmap->port_csk) {
848c2ecf20Sopenharmony_ci		pr_warn("cdev 0x%p, portmap OOM %u.\n", cdev, max_conn);
858c2ecf20Sopenharmony_ci		return -ENOMEM;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	pmap->max_connect = max_conn;
898c2ecf20Sopenharmony_ci	pmap->sport_base = base;
908c2ecf20Sopenharmony_ci	spin_lock_init(&pmap->lock);
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_device_portmap_create);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_civoid cxgbi_device_portmap_cleanup(struct cxgbi_device *cdev)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct cxgbi_ports_map *pmap = &cdev->pmap;
988c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk;
998c2ecf20Sopenharmony_ci	int i;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	for (i = 0; i < pmap->max_connect; i++) {
1028c2ecf20Sopenharmony_ci		if (pmap->port_csk[i]) {
1038c2ecf20Sopenharmony_ci			csk = pmap->port_csk[i];
1048c2ecf20Sopenharmony_ci			pmap->port_csk[i] = NULL;
1058c2ecf20Sopenharmony_ci			log_debug(1 << CXGBI_DBG_SOCK,
1068c2ecf20Sopenharmony_ci				"csk 0x%p, cdev 0x%p, offload down.\n",
1078c2ecf20Sopenharmony_ci				csk, cdev);
1088c2ecf20Sopenharmony_ci			spin_lock_bh(&csk->lock);
1098c2ecf20Sopenharmony_ci			cxgbi_sock_set_flag(csk, CTPF_OFFLOAD_DOWN);
1108c2ecf20Sopenharmony_ci			cxgbi_sock_closed(csk);
1118c2ecf20Sopenharmony_ci			spin_unlock_bh(&csk->lock);
1128c2ecf20Sopenharmony_ci			cxgbi_sock_put(csk);
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_device_portmap_cleanup);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic inline void cxgbi_device_destroy(struct cxgbi_device *cdev)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV,
1218c2ecf20Sopenharmony_ci		"cdev 0x%p, p# %u.\n", cdev, cdev->nports);
1228c2ecf20Sopenharmony_ci	cxgbi_hbas_remove(cdev);
1238c2ecf20Sopenharmony_ci	cxgbi_device_portmap_cleanup(cdev);
1248c2ecf20Sopenharmony_ci	if (cdev->cdev2ppm)
1258c2ecf20Sopenharmony_ci		cxgbi_ppm_release(cdev->cdev2ppm(cdev));
1268c2ecf20Sopenharmony_ci	if (cdev->pmap.max_connect)
1278c2ecf20Sopenharmony_ci		kvfree(cdev->pmap.port_csk);
1288c2ecf20Sopenharmony_ci	kfree(cdev);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistruct cxgbi_device *cxgbi_device_register(unsigned int extra,
1328c2ecf20Sopenharmony_ci					   unsigned int nports)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	cdev = kzalloc(sizeof(*cdev) + extra + nports *
1378c2ecf20Sopenharmony_ci			(sizeof(struct cxgbi_hba *) +
1388c2ecf20Sopenharmony_ci			 sizeof(struct net_device *)),
1398c2ecf20Sopenharmony_ci			GFP_KERNEL);
1408c2ecf20Sopenharmony_ci	if (!cdev) {
1418c2ecf20Sopenharmony_ci		pr_warn("nport %d, OOM.\n", nports);
1428c2ecf20Sopenharmony_ci		return NULL;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci	cdev->ports = (struct net_device **)(cdev + 1);
1458c2ecf20Sopenharmony_ci	cdev->hbas = (struct cxgbi_hba **)(((char*)cdev->ports) + nports *
1468c2ecf20Sopenharmony_ci						sizeof(struct net_device *));
1478c2ecf20Sopenharmony_ci	if (extra)
1488c2ecf20Sopenharmony_ci		cdev->dd_data = ((char *)cdev->hbas) +
1498c2ecf20Sopenharmony_ci				nports * sizeof(struct cxgbi_hba *);
1508c2ecf20Sopenharmony_ci	spin_lock_init(&cdev->pmap.lock);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	mutex_lock(&cdev_mutex);
1538c2ecf20Sopenharmony_ci	list_add_tail(&cdev->list_head, &cdev_list);
1548c2ecf20Sopenharmony_ci	mutex_unlock(&cdev_mutex);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	spin_lock(&cdev_rcu_lock);
1578c2ecf20Sopenharmony_ci	list_add_tail_rcu(&cdev->rcu_node, &cdev_rcu_list);
1588c2ecf20Sopenharmony_ci	spin_unlock(&cdev_rcu_lock);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV,
1618c2ecf20Sopenharmony_ci		"cdev 0x%p, p# %u.\n", cdev, nports);
1628c2ecf20Sopenharmony_ci	return cdev;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_device_register);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_civoid cxgbi_device_unregister(struct cxgbi_device *cdev)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV,
1698c2ecf20Sopenharmony_ci		"cdev 0x%p, p# %u,%s.\n",
1708c2ecf20Sopenharmony_ci		cdev, cdev->nports, cdev->nports ? cdev->ports[0]->name : "");
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	mutex_lock(&cdev_mutex);
1738c2ecf20Sopenharmony_ci	list_del(&cdev->list_head);
1748c2ecf20Sopenharmony_ci	mutex_unlock(&cdev_mutex);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	spin_lock(&cdev_rcu_lock);
1778c2ecf20Sopenharmony_ci	list_del_rcu(&cdev->rcu_node);
1788c2ecf20Sopenharmony_ci	spin_unlock(&cdev_rcu_lock);
1798c2ecf20Sopenharmony_ci	synchronize_rcu();
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	cxgbi_device_destroy(cdev);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_device_unregister);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_civoid cxgbi_device_unregister_all(unsigned int flag)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev, *tmp;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	mutex_lock(&cdev_mutex);
1908c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
1918c2ecf20Sopenharmony_ci		if ((cdev->flags & flag) == flag) {
1928c2ecf20Sopenharmony_ci			mutex_unlock(&cdev_mutex);
1938c2ecf20Sopenharmony_ci			cxgbi_device_unregister(cdev);
1948c2ecf20Sopenharmony_ci			mutex_lock(&cdev_mutex);
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci	mutex_unlock(&cdev_mutex);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_device_unregister_all);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistruct cxgbi_device *cxgbi_device_find_by_lldev(void *lldev)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev, *tmp;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	mutex_lock(&cdev_mutex);
2068c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
2078c2ecf20Sopenharmony_ci		if (cdev->lldev == lldev) {
2088c2ecf20Sopenharmony_ci			mutex_unlock(&cdev_mutex);
2098c2ecf20Sopenharmony_ci			return cdev;
2108c2ecf20Sopenharmony_ci		}
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci	mutex_unlock(&cdev_mutex);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV,
2158c2ecf20Sopenharmony_ci		"lldev 0x%p, NO match found.\n", lldev);
2168c2ecf20Sopenharmony_ci	return NULL;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_device_find_by_lldev);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistruct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
2218c2ecf20Sopenharmony_ci						 int *port)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct net_device *vdev = NULL;
2248c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev, *tmp;
2258c2ecf20Sopenharmony_ci	int i;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (is_vlan_dev(ndev)) {
2288c2ecf20Sopenharmony_ci		vdev = ndev;
2298c2ecf20Sopenharmony_ci		ndev = vlan_dev_real_dev(ndev);
2308c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_DEV,
2318c2ecf20Sopenharmony_ci			"vlan dev %s -> %s.\n", vdev->name, ndev->name);
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	mutex_lock(&cdev_mutex);
2358c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
2368c2ecf20Sopenharmony_ci		for (i = 0; i < cdev->nports; i++) {
2378c2ecf20Sopenharmony_ci			if (ndev == cdev->ports[i]) {
2388c2ecf20Sopenharmony_ci				cdev->hbas[i]->vdev = vdev;
2398c2ecf20Sopenharmony_ci				mutex_unlock(&cdev_mutex);
2408c2ecf20Sopenharmony_ci				if (port)
2418c2ecf20Sopenharmony_ci					*port = i;
2428c2ecf20Sopenharmony_ci				return cdev;
2438c2ecf20Sopenharmony_ci			}
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci	mutex_unlock(&cdev_mutex);
2478c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV,
2488c2ecf20Sopenharmony_ci		"ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
2498c2ecf20Sopenharmony_ci	return NULL;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistruct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *ndev,
2548c2ecf20Sopenharmony_ci						     int *port)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct net_device *vdev = NULL;
2578c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev;
2588c2ecf20Sopenharmony_ci	int i;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (is_vlan_dev(ndev)) {
2618c2ecf20Sopenharmony_ci		vdev = ndev;
2628c2ecf20Sopenharmony_ci		ndev = vlan_dev_real_dev(ndev);
2638c2ecf20Sopenharmony_ci		pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	rcu_read_lock();
2678c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(cdev, &cdev_rcu_list, rcu_node) {
2688c2ecf20Sopenharmony_ci		for (i = 0; i < cdev->nports; i++) {
2698c2ecf20Sopenharmony_ci			if (ndev == cdev->ports[i]) {
2708c2ecf20Sopenharmony_ci				cdev->hbas[i]->vdev = vdev;
2718c2ecf20Sopenharmony_ci				rcu_read_unlock();
2728c2ecf20Sopenharmony_ci				if (port)
2738c2ecf20Sopenharmony_ci					*port = i;
2748c2ecf20Sopenharmony_ci				return cdev;
2758c2ecf20Sopenharmony_ci			}
2768c2ecf20Sopenharmony_ci		}
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci	rcu_read_unlock();
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV,
2818c2ecf20Sopenharmony_ci		  "ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
2828c2ecf20Sopenharmony_ci	return NULL;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev_rcu);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev,
2878c2ecf20Sopenharmony_ci						     int *port)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct net_device *vdev = NULL;
2908c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev, *tmp;
2918c2ecf20Sopenharmony_ci	int i;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (is_vlan_dev(ndev)) {
2948c2ecf20Sopenharmony_ci		vdev = ndev;
2958c2ecf20Sopenharmony_ci		ndev = vlan_dev_real_dev(ndev);
2968c2ecf20Sopenharmony_ci		pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	mutex_lock(&cdev_mutex);
3008c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
3018c2ecf20Sopenharmony_ci		for (i = 0; i < cdev->nports; i++) {
3028c2ecf20Sopenharmony_ci			if (!memcmp(ndev->dev_addr, cdev->ports[i]->dev_addr,
3038c2ecf20Sopenharmony_ci				    MAX_ADDR_LEN)) {
3048c2ecf20Sopenharmony_ci				cdev->hbas[i]->vdev = vdev;
3058c2ecf20Sopenharmony_ci				mutex_unlock(&cdev_mutex);
3068c2ecf20Sopenharmony_ci				if (port)
3078c2ecf20Sopenharmony_ci					*port = i;
3088c2ecf20Sopenharmony_ci				return cdev;
3098c2ecf20Sopenharmony_ci			}
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci	mutex_unlock(&cdev_mutex);
3138c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV,
3148c2ecf20Sopenharmony_ci		  "ndev 0x%p, %s, NO match mac found.\n",
3158c2ecf20Sopenharmony_ci		  ndev, ndev->name);
3168c2ecf20Sopenharmony_ci	return NULL;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_civoid cxgbi_hbas_remove(struct cxgbi_device *cdev)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	int i;
3228c2ecf20Sopenharmony_ci	struct cxgbi_hba *chba;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV,
3258c2ecf20Sopenharmony_ci		"cdev 0x%p, p#%u.\n", cdev, cdev->nports);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (i = 0; i < cdev->nports; i++) {
3288c2ecf20Sopenharmony_ci		chba = cdev->hbas[i];
3298c2ecf20Sopenharmony_ci		if (chba) {
3308c2ecf20Sopenharmony_ci			cdev->hbas[i] = NULL;
3318c2ecf20Sopenharmony_ci			iscsi_host_remove(chba->shost);
3328c2ecf20Sopenharmony_ci			pci_dev_put(cdev->pdev);
3338c2ecf20Sopenharmony_ci			iscsi_host_free(chba->shost);
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_hbas_remove);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ciint cxgbi_hbas_add(struct cxgbi_device *cdev, u64 max_lun,
3408c2ecf20Sopenharmony_ci		unsigned int max_conns, struct scsi_host_template *sht,
3418c2ecf20Sopenharmony_ci		struct scsi_transport_template *stt)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct cxgbi_hba *chba;
3448c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
3458c2ecf20Sopenharmony_ci	int i, err;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DEV, "cdev 0x%p, p#%u.\n", cdev, cdev->nports);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	for (i = 0; i < cdev->nports; i++) {
3508c2ecf20Sopenharmony_ci		shost = iscsi_host_alloc(sht, sizeof(*chba), 1);
3518c2ecf20Sopenharmony_ci		if (!shost) {
3528c2ecf20Sopenharmony_ci			pr_info("0x%p, p%d, %s, host alloc failed.\n",
3538c2ecf20Sopenharmony_ci				cdev, i, cdev->ports[i]->name);
3548c2ecf20Sopenharmony_ci			err = -ENOMEM;
3558c2ecf20Sopenharmony_ci			goto err_out;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		shost->transportt = stt;
3598c2ecf20Sopenharmony_ci		shost->max_lun = max_lun;
3608c2ecf20Sopenharmony_ci		shost->max_id = max_conns - 1;
3618c2ecf20Sopenharmony_ci		shost->max_channel = 0;
3628c2ecf20Sopenharmony_ci		shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		chba = iscsi_host_priv(shost);
3658c2ecf20Sopenharmony_ci		chba->cdev = cdev;
3668c2ecf20Sopenharmony_ci		chba->ndev = cdev->ports[i];
3678c2ecf20Sopenharmony_ci		chba->shost = shost;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		shost->can_queue = sht->can_queue - ISCSI_MGMT_CMDS_MAX;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_DEV,
3728c2ecf20Sopenharmony_ci			"cdev 0x%p, p#%d %s: chba 0x%p.\n",
3738c2ecf20Sopenharmony_ci			cdev, i, cdev->ports[i]->name, chba);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		pci_dev_get(cdev->pdev);
3768c2ecf20Sopenharmony_ci		err = iscsi_host_add(shost, &cdev->pdev->dev);
3778c2ecf20Sopenharmony_ci		if (err) {
3788c2ecf20Sopenharmony_ci			pr_info("cdev 0x%p, p#%d %s, host add failed.\n",
3798c2ecf20Sopenharmony_ci				cdev, i, cdev->ports[i]->name);
3808c2ecf20Sopenharmony_ci			pci_dev_put(cdev->pdev);
3818c2ecf20Sopenharmony_ci			scsi_host_put(shost);
3828c2ecf20Sopenharmony_ci			goto  err_out;
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		cdev->hbas[i] = chba;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cierr_out:
3918c2ecf20Sopenharmony_ci	cxgbi_hbas_remove(cdev);
3928c2ecf20Sopenharmony_ci	return err;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_hbas_add);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci/*
3978c2ecf20Sopenharmony_ci * iSCSI offload
3988c2ecf20Sopenharmony_ci *
3998c2ecf20Sopenharmony_ci * - source port management
4008c2ecf20Sopenharmony_ci *   To find a free source port in the port allocation map we use a very simple
4018c2ecf20Sopenharmony_ci *   rotor scheme to look for the next free port.
4028c2ecf20Sopenharmony_ci *
4038c2ecf20Sopenharmony_ci *   If a source port has been specified make sure that it doesn't collide with
4048c2ecf20Sopenharmony_ci *   our normal source port allocation map.  If it's outside the range of our
4058c2ecf20Sopenharmony_ci *   allocation/deallocation scheme just let them use it.
4068c2ecf20Sopenharmony_ci *
4078c2ecf20Sopenharmony_ci *   If the source port is outside our allocation range, the caller is
4088c2ecf20Sopenharmony_ci *   responsible for keeping track of their port usage.
4098c2ecf20Sopenharmony_ci */
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic struct cxgbi_sock *find_sock_on_port(struct cxgbi_device *cdev,
4128c2ecf20Sopenharmony_ci					    unsigned char port_id)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct cxgbi_ports_map *pmap = &cdev->pmap;
4158c2ecf20Sopenharmony_ci	unsigned int i;
4168c2ecf20Sopenharmony_ci	unsigned int used;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (!pmap->max_connect || !pmap->used)
4198c2ecf20Sopenharmony_ci		return NULL;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	spin_lock_bh(&pmap->lock);
4228c2ecf20Sopenharmony_ci	used = pmap->used;
4238c2ecf20Sopenharmony_ci	for (i = 0; used && i < pmap->max_connect; i++) {
4248c2ecf20Sopenharmony_ci		struct cxgbi_sock *csk = pmap->port_csk[i];
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		if (csk) {
4278c2ecf20Sopenharmony_ci			if (csk->port_id == port_id) {
4288c2ecf20Sopenharmony_ci				spin_unlock_bh(&pmap->lock);
4298c2ecf20Sopenharmony_ci				return csk;
4308c2ecf20Sopenharmony_ci			}
4318c2ecf20Sopenharmony_ci			used--;
4328c2ecf20Sopenharmony_ci		}
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci	spin_unlock_bh(&pmap->lock);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return NULL;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int sock_get_port(struct cxgbi_sock *csk)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = csk->cdev;
4428c2ecf20Sopenharmony_ci	struct cxgbi_ports_map *pmap = &cdev->pmap;
4438c2ecf20Sopenharmony_ci	unsigned int start;
4448c2ecf20Sopenharmony_ci	int idx;
4458c2ecf20Sopenharmony_ci	__be16 *port;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (!pmap->max_connect) {
4488c2ecf20Sopenharmony_ci		pr_err("cdev 0x%p, p#%u %s, NO port map.\n",
4498c2ecf20Sopenharmony_ci			   cdev, csk->port_id, cdev->ports[csk->port_id]->name);
4508c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (csk->csk_family == AF_INET)
4548c2ecf20Sopenharmony_ci		port = &csk->saddr.sin_port;
4558c2ecf20Sopenharmony_ci	else /* ipv6 */
4568c2ecf20Sopenharmony_ci		port = &csk->saddr6.sin6_port;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (*port) {
4598c2ecf20Sopenharmony_ci		pr_err("source port NON-ZERO %u.\n",
4608c2ecf20Sopenharmony_ci			ntohs(*port));
4618c2ecf20Sopenharmony_ci		return -EADDRINUSE;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	spin_lock_bh(&pmap->lock);
4658c2ecf20Sopenharmony_ci	if (pmap->used >= pmap->max_connect) {
4668c2ecf20Sopenharmony_ci		spin_unlock_bh(&pmap->lock);
4678c2ecf20Sopenharmony_ci		pr_info("cdev 0x%p, p#%u %s, ALL ports used.\n",
4688c2ecf20Sopenharmony_ci			cdev, csk->port_id, cdev->ports[csk->port_id]->name);
4698c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	start = idx = pmap->next;
4738c2ecf20Sopenharmony_ci	do {
4748c2ecf20Sopenharmony_ci		if (++idx >= pmap->max_connect)
4758c2ecf20Sopenharmony_ci			idx = 0;
4768c2ecf20Sopenharmony_ci		if (!pmap->port_csk[idx]) {
4778c2ecf20Sopenharmony_ci			pmap->used++;
4788c2ecf20Sopenharmony_ci			*port = htons(pmap->sport_base + idx);
4798c2ecf20Sopenharmony_ci			pmap->next = idx;
4808c2ecf20Sopenharmony_ci			pmap->port_csk[idx] = csk;
4818c2ecf20Sopenharmony_ci			spin_unlock_bh(&pmap->lock);
4828c2ecf20Sopenharmony_ci			cxgbi_sock_get(csk);
4838c2ecf20Sopenharmony_ci			log_debug(1 << CXGBI_DBG_SOCK,
4848c2ecf20Sopenharmony_ci				"cdev 0x%p, p#%u %s, p %u, %u.\n",
4858c2ecf20Sopenharmony_ci				cdev, csk->port_id,
4868c2ecf20Sopenharmony_ci				cdev->ports[csk->port_id]->name,
4878c2ecf20Sopenharmony_ci				pmap->sport_base + idx, pmap->next);
4888c2ecf20Sopenharmony_ci			return 0;
4898c2ecf20Sopenharmony_ci		}
4908c2ecf20Sopenharmony_ci	} while (idx != start);
4918c2ecf20Sopenharmony_ci	spin_unlock_bh(&pmap->lock);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* should not happen */
4948c2ecf20Sopenharmony_ci	pr_warn("cdev 0x%p, p#%u %s, next %u?\n",
4958c2ecf20Sopenharmony_ci		cdev, csk->port_id, cdev->ports[csk->port_id]->name,
4968c2ecf20Sopenharmony_ci		pmap->next);
4978c2ecf20Sopenharmony_ci	return -EADDRNOTAVAIL;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic void sock_put_port(struct cxgbi_sock *csk)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = csk->cdev;
5038c2ecf20Sopenharmony_ci	struct cxgbi_ports_map *pmap = &cdev->pmap;
5048c2ecf20Sopenharmony_ci	__be16 *port;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (csk->csk_family == AF_INET)
5078c2ecf20Sopenharmony_ci		port = &csk->saddr.sin_port;
5088c2ecf20Sopenharmony_ci	else /* ipv6 */
5098c2ecf20Sopenharmony_ci		port = &csk->saddr6.sin6_port;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (*port) {
5128c2ecf20Sopenharmony_ci		int idx = ntohs(*port) - pmap->sport_base;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci		*port = 0;
5158c2ecf20Sopenharmony_ci		if (idx < 0 || idx >= pmap->max_connect) {
5168c2ecf20Sopenharmony_ci			pr_err("cdev 0x%p, p#%u %s, port %u OOR.\n",
5178c2ecf20Sopenharmony_ci				cdev, csk->port_id,
5188c2ecf20Sopenharmony_ci				cdev->ports[csk->port_id]->name,
5198c2ecf20Sopenharmony_ci				ntohs(*port));
5208c2ecf20Sopenharmony_ci			return;
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		spin_lock_bh(&pmap->lock);
5248c2ecf20Sopenharmony_ci		pmap->port_csk[idx] = NULL;
5258c2ecf20Sopenharmony_ci		pmap->used--;
5268c2ecf20Sopenharmony_ci		spin_unlock_bh(&pmap->lock);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_SOCK,
5298c2ecf20Sopenharmony_ci			"cdev 0x%p, p#%u %s, release %u.\n",
5308c2ecf20Sopenharmony_ci			cdev, csk->port_id, cdev->ports[csk->port_id]->name,
5318c2ecf20Sopenharmony_ci			pmap->sport_base + idx);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		cxgbi_sock_put(csk);
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci/*
5388c2ecf20Sopenharmony_ci * iscsi tcp connection
5398c2ecf20Sopenharmony_ci */
5408c2ecf20Sopenharmony_civoid cxgbi_sock_free_cpl_skbs(struct cxgbi_sock *csk)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	if (csk->cpl_close) {
5438c2ecf20Sopenharmony_ci		kfree_skb(csk->cpl_close);
5448c2ecf20Sopenharmony_ci		csk->cpl_close = NULL;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci	if (csk->cpl_abort_req) {
5478c2ecf20Sopenharmony_ci		kfree_skb(csk->cpl_abort_req);
5488c2ecf20Sopenharmony_ci		csk->cpl_abort_req = NULL;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci	if (csk->cpl_abort_rpl) {
5518c2ecf20Sopenharmony_ci		kfree_skb(csk->cpl_abort_rpl);
5528c2ecf20Sopenharmony_ci		csk->cpl_abort_rpl = NULL;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_free_cpl_skbs);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic struct cxgbi_sock *cxgbi_sock_create(struct cxgbi_device *cdev)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = kzalloc(sizeof(*csk), GFP_NOIO);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (!csk) {
5628c2ecf20Sopenharmony_ci		pr_info("alloc csk %zu failed.\n", sizeof(*csk));
5638c2ecf20Sopenharmony_ci		return NULL;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (cdev->csk_alloc_cpls(csk) < 0) {
5678c2ecf20Sopenharmony_ci		pr_info("csk 0x%p, alloc cpls failed.\n", csk);
5688c2ecf20Sopenharmony_ci		kfree(csk);
5698c2ecf20Sopenharmony_ci		return NULL;
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	spin_lock_init(&csk->lock);
5738c2ecf20Sopenharmony_ci	kref_init(&csk->refcnt);
5748c2ecf20Sopenharmony_ci	skb_queue_head_init(&csk->receive_queue);
5758c2ecf20Sopenharmony_ci	skb_queue_head_init(&csk->write_queue);
5768c2ecf20Sopenharmony_ci	timer_setup(&csk->retry_timer, NULL, 0);
5778c2ecf20Sopenharmony_ci	init_completion(&csk->cmpl);
5788c2ecf20Sopenharmony_ci	rwlock_init(&csk->callback_lock);
5798c2ecf20Sopenharmony_ci	csk->cdev = cdev;
5808c2ecf20Sopenharmony_ci	csk->flags = 0;
5818c2ecf20Sopenharmony_ci	cxgbi_sock_set_state(csk, CTP_CLOSED);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK, "cdev 0x%p, new csk 0x%p.\n", cdev, csk);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	return csk;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic struct rtable *find_route_ipv4(struct flowi4 *fl4,
5898c2ecf20Sopenharmony_ci				      __be32 saddr, __be32 daddr,
5908c2ecf20Sopenharmony_ci				      __be16 sport, __be16 dport, u8 tos,
5918c2ecf20Sopenharmony_ci				      int ifindex)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	struct rtable *rt;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	rt = ip_route_output_ports(&init_net, fl4, NULL, daddr, saddr,
5968c2ecf20Sopenharmony_ci				   dport, sport, IPPROTO_TCP, tos, ifindex);
5978c2ecf20Sopenharmony_ci	if (IS_ERR(rt))
5988c2ecf20Sopenharmony_ci		return NULL;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	return rt;
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic struct cxgbi_sock *
6048c2ecf20Sopenharmony_cicxgbi_check_route(struct sockaddr *dst_addr, int ifindex)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct sockaddr_in *daddr = (struct sockaddr_in *)dst_addr;
6078c2ecf20Sopenharmony_ci	struct dst_entry *dst;
6088c2ecf20Sopenharmony_ci	struct net_device *ndev;
6098c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev;
6108c2ecf20Sopenharmony_ci	struct rtable *rt = NULL;
6118c2ecf20Sopenharmony_ci	struct neighbour *n;
6128c2ecf20Sopenharmony_ci	struct flowi4 fl4;
6138c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = NULL;
6148c2ecf20Sopenharmony_ci	unsigned int mtu = 0;
6158c2ecf20Sopenharmony_ci	int port = 0xFFFF;
6168c2ecf20Sopenharmony_ci	int err = 0;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	rt = find_route_ipv4(&fl4, 0, daddr->sin_addr.s_addr, 0,
6198c2ecf20Sopenharmony_ci			     daddr->sin_port, 0, ifindex);
6208c2ecf20Sopenharmony_ci	if (!rt) {
6218c2ecf20Sopenharmony_ci		pr_info("no route to ipv4 0x%x, port %u.\n",
6228c2ecf20Sopenharmony_ci			be32_to_cpu(daddr->sin_addr.s_addr),
6238c2ecf20Sopenharmony_ci			be16_to_cpu(daddr->sin_port));
6248c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
6258c2ecf20Sopenharmony_ci		goto err_out;
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci	dst = &rt->dst;
6288c2ecf20Sopenharmony_ci	n = dst_neigh_lookup(dst, &daddr->sin_addr.s_addr);
6298c2ecf20Sopenharmony_ci	if (!n) {
6308c2ecf20Sopenharmony_ci		err = -ENODEV;
6318c2ecf20Sopenharmony_ci		goto rel_rt;
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci	ndev = n->dev;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
6368c2ecf20Sopenharmony_ci		pr_info("multi-cast route %pI4, port %u, dev %s.\n",
6378c2ecf20Sopenharmony_ci			&daddr->sin_addr.s_addr, ntohs(daddr->sin_port),
6388c2ecf20Sopenharmony_ci			ndev->name);
6398c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
6408c2ecf20Sopenharmony_ci		goto rel_neigh;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (ndev->flags & IFF_LOOPBACK) {
6448c2ecf20Sopenharmony_ci		ndev = ip_dev_find(&init_net, daddr->sin_addr.s_addr);
6458c2ecf20Sopenharmony_ci		if (!ndev) {
6468c2ecf20Sopenharmony_ci			err = -ENETUNREACH;
6478c2ecf20Sopenharmony_ci			goto rel_neigh;
6488c2ecf20Sopenharmony_ci		}
6498c2ecf20Sopenharmony_ci		mtu = ndev->mtu;
6508c2ecf20Sopenharmony_ci		pr_info("rt dev %s, loopback -> %s, mtu %u.\n",
6518c2ecf20Sopenharmony_ci			n->dev->name, ndev->name, mtu);
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (!(ndev->flags & IFF_UP) || !netif_carrier_ok(ndev)) {
6558c2ecf20Sopenharmony_ci		pr_info("%s interface not up.\n", ndev->name);
6568c2ecf20Sopenharmony_ci		err = -ENETDOWN;
6578c2ecf20Sopenharmony_ci		goto rel_neigh;
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	cdev = cxgbi_device_find_by_netdev(ndev, &port);
6618c2ecf20Sopenharmony_ci	if (!cdev)
6628c2ecf20Sopenharmony_ci		cdev = cxgbi_device_find_by_mac(ndev, &port);
6638c2ecf20Sopenharmony_ci	if (!cdev) {
6648c2ecf20Sopenharmony_ci		pr_info("dst %pI4, %s, NOT cxgbi device.\n",
6658c2ecf20Sopenharmony_ci			&daddr->sin_addr.s_addr, ndev->name);
6668c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
6678c2ecf20Sopenharmony_ci		goto rel_neigh;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK,
6708c2ecf20Sopenharmony_ci		"route to %pI4 :%u, ndev p#%d,%s, cdev 0x%p.\n",
6718c2ecf20Sopenharmony_ci		&daddr->sin_addr.s_addr, ntohs(daddr->sin_port),
6728c2ecf20Sopenharmony_ci			   port, ndev->name, cdev);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	csk = cxgbi_sock_create(cdev);
6758c2ecf20Sopenharmony_ci	if (!csk) {
6768c2ecf20Sopenharmony_ci		err = -ENOMEM;
6778c2ecf20Sopenharmony_ci		goto rel_neigh;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	csk->cdev = cdev;
6808c2ecf20Sopenharmony_ci	csk->port_id = port;
6818c2ecf20Sopenharmony_ci	csk->mtu = mtu;
6828c2ecf20Sopenharmony_ci	csk->dst = dst;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	csk->csk_family = AF_INET;
6858c2ecf20Sopenharmony_ci	csk->daddr.sin_addr.s_addr = daddr->sin_addr.s_addr;
6868c2ecf20Sopenharmony_ci	csk->daddr.sin_port = daddr->sin_port;
6878c2ecf20Sopenharmony_ci	csk->daddr.sin_family = daddr->sin_family;
6888c2ecf20Sopenharmony_ci	csk->saddr.sin_family = daddr->sin_family;
6898c2ecf20Sopenharmony_ci	csk->saddr.sin_addr.s_addr = fl4.saddr;
6908c2ecf20Sopenharmony_ci	neigh_release(n);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return csk;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cirel_neigh:
6958c2ecf20Sopenharmony_ci	neigh_release(n);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cirel_rt:
6988c2ecf20Sopenharmony_ci	ip_rt_put(rt);
6998c2ecf20Sopenharmony_cierr_out:
7008c2ecf20Sopenharmony_ci	return ERR_PTR(err);
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
7048c2ecf20Sopenharmony_cistatic struct rt6_info *find_route_ipv6(const struct in6_addr *saddr,
7058c2ecf20Sopenharmony_ci					const struct in6_addr *daddr,
7068c2ecf20Sopenharmony_ci					int ifindex)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct flowi6 fl;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	memset(&fl, 0, sizeof(fl));
7118c2ecf20Sopenharmony_ci	fl.flowi6_oif = ifindex;
7128c2ecf20Sopenharmony_ci	if (saddr)
7138c2ecf20Sopenharmony_ci		memcpy(&fl.saddr, saddr, sizeof(struct in6_addr));
7148c2ecf20Sopenharmony_ci	if (daddr)
7158c2ecf20Sopenharmony_ci		memcpy(&fl.daddr, daddr, sizeof(struct in6_addr));
7168c2ecf20Sopenharmony_ci	return (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic struct cxgbi_sock *
7208c2ecf20Sopenharmony_cicxgbi_check_route6(struct sockaddr *dst_addr, int ifindex)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct sockaddr_in6 *daddr6 = (struct sockaddr_in6 *)dst_addr;
7238c2ecf20Sopenharmony_ci	struct dst_entry *dst;
7248c2ecf20Sopenharmony_ci	struct net_device *ndev;
7258c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev;
7268c2ecf20Sopenharmony_ci	struct rt6_info *rt = NULL;
7278c2ecf20Sopenharmony_ci	struct neighbour *n;
7288c2ecf20Sopenharmony_ci	struct in6_addr pref_saddr;
7298c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = NULL;
7308c2ecf20Sopenharmony_ci	unsigned int mtu = 0;
7318c2ecf20Sopenharmony_ci	int port = 0xFFFF;
7328c2ecf20Sopenharmony_ci	int err = 0;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	rt = find_route_ipv6(NULL, &daddr6->sin6_addr, ifindex);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (!rt) {
7378c2ecf20Sopenharmony_ci		pr_info("no route to ipv6 %pI6 port %u\n",
7388c2ecf20Sopenharmony_ci			daddr6->sin6_addr.s6_addr,
7398c2ecf20Sopenharmony_ci			be16_to_cpu(daddr6->sin6_port));
7408c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
7418c2ecf20Sopenharmony_ci		goto err_out;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	dst = &rt->dst;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	n = dst_neigh_lookup(dst, &daddr6->sin6_addr);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if (!n) {
7498c2ecf20Sopenharmony_ci		pr_info("%pI6, port %u, dst no neighbour.\n",
7508c2ecf20Sopenharmony_ci			daddr6->sin6_addr.s6_addr,
7518c2ecf20Sopenharmony_ci			be16_to_cpu(daddr6->sin6_port));
7528c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
7538c2ecf20Sopenharmony_ci		goto rel_rt;
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci	ndev = n->dev;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (!(ndev->flags & IFF_UP) || !netif_carrier_ok(ndev)) {
7588c2ecf20Sopenharmony_ci		pr_info("%s interface not up.\n", ndev->name);
7598c2ecf20Sopenharmony_ci		err = -ENETDOWN;
7608c2ecf20Sopenharmony_ci		goto rel_rt;
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (ipv6_addr_is_multicast(&daddr6->sin6_addr)) {
7648c2ecf20Sopenharmony_ci		pr_info("multi-cast route %pI6 port %u, dev %s.\n",
7658c2ecf20Sopenharmony_ci			daddr6->sin6_addr.s6_addr,
7668c2ecf20Sopenharmony_ci			ntohs(daddr6->sin6_port), ndev->name);
7678c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
7688c2ecf20Sopenharmony_ci		goto rel_rt;
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	cdev = cxgbi_device_find_by_netdev(ndev, &port);
7728c2ecf20Sopenharmony_ci	if (!cdev)
7738c2ecf20Sopenharmony_ci		cdev = cxgbi_device_find_by_mac(ndev, &port);
7748c2ecf20Sopenharmony_ci	if (!cdev) {
7758c2ecf20Sopenharmony_ci		pr_info("dst %pI6 %s, NOT cxgbi device.\n",
7768c2ecf20Sopenharmony_ci			daddr6->sin6_addr.s6_addr, ndev->name);
7778c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
7788c2ecf20Sopenharmony_ci		goto rel_rt;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK,
7818c2ecf20Sopenharmony_ci		  "route to %pI6 :%u, ndev p#%d,%s, cdev 0x%p.\n",
7828c2ecf20Sopenharmony_ci		  daddr6->sin6_addr.s6_addr, ntohs(daddr6->sin6_port), port,
7838c2ecf20Sopenharmony_ci		  ndev->name, cdev);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	csk = cxgbi_sock_create(cdev);
7868c2ecf20Sopenharmony_ci	if (!csk) {
7878c2ecf20Sopenharmony_ci		err = -ENOMEM;
7888c2ecf20Sopenharmony_ci		goto rel_rt;
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci	csk->cdev = cdev;
7918c2ecf20Sopenharmony_ci	csk->port_id = port;
7928c2ecf20Sopenharmony_ci	csk->mtu = mtu;
7938c2ecf20Sopenharmony_ci	csk->dst = dst;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	rt6_get_prefsrc(rt, &pref_saddr);
7968c2ecf20Sopenharmony_ci	if (ipv6_addr_any(&pref_saddr)) {
7978c2ecf20Sopenharmony_ci		struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci		err = ipv6_dev_get_saddr(&init_net, idev ? idev->dev : NULL,
8008c2ecf20Sopenharmony_ci					 &daddr6->sin6_addr, 0, &pref_saddr);
8018c2ecf20Sopenharmony_ci		if (err) {
8028c2ecf20Sopenharmony_ci			pr_info("failed to get source address to reach %pI6\n",
8038c2ecf20Sopenharmony_ci				&daddr6->sin6_addr);
8048c2ecf20Sopenharmony_ci			goto rel_rt;
8058c2ecf20Sopenharmony_ci		}
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	csk->csk_family = AF_INET6;
8098c2ecf20Sopenharmony_ci	csk->daddr6.sin6_addr = daddr6->sin6_addr;
8108c2ecf20Sopenharmony_ci	csk->daddr6.sin6_port = daddr6->sin6_port;
8118c2ecf20Sopenharmony_ci	csk->daddr6.sin6_family = daddr6->sin6_family;
8128c2ecf20Sopenharmony_ci	csk->saddr6.sin6_family = daddr6->sin6_family;
8138c2ecf20Sopenharmony_ci	csk->saddr6.sin6_addr = pref_saddr;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	neigh_release(n);
8168c2ecf20Sopenharmony_ci	return csk;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cirel_rt:
8198c2ecf20Sopenharmony_ci	if (n)
8208c2ecf20Sopenharmony_ci		neigh_release(n);
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	ip6_rt_put(rt);
8238c2ecf20Sopenharmony_ci	if (csk)
8248c2ecf20Sopenharmony_ci		cxgbi_sock_closed(csk);
8258c2ecf20Sopenharmony_cierr_out:
8268c2ecf20Sopenharmony_ci	return ERR_PTR(err);
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_IPV6) */
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_civoid cxgbi_sock_established(struct cxgbi_sock *csk, unsigned int snd_isn,
8318c2ecf20Sopenharmony_ci			unsigned int opt)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	csk->write_seq = csk->snd_nxt = csk->snd_una = snd_isn;
8348c2ecf20Sopenharmony_ci	dst_confirm(csk->dst);
8358c2ecf20Sopenharmony_ci	smp_mb();
8368c2ecf20Sopenharmony_ci	cxgbi_sock_set_state(csk, CTP_ESTABLISHED);
8378c2ecf20Sopenharmony_ci}
8388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_established);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic void cxgbi_inform_iscsi_conn_closing(struct cxgbi_sock *csk)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK,
8438c2ecf20Sopenharmony_ci		"csk 0x%p, state %u, flags 0x%lx, conn 0x%p.\n",
8448c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->user_data);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	if (csk->state != CTP_ESTABLISHED) {
8478c2ecf20Sopenharmony_ci		read_lock_bh(&csk->callback_lock);
8488c2ecf20Sopenharmony_ci		if (csk->user_data)
8498c2ecf20Sopenharmony_ci			iscsi_conn_failure(csk->user_data,
8508c2ecf20Sopenharmony_ci					ISCSI_ERR_TCP_CONN_CLOSE);
8518c2ecf20Sopenharmony_ci		read_unlock_bh(&csk->callback_lock);
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_civoid cxgbi_sock_closed(struct cxgbi_sock *csk)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n",
8588c2ecf20Sopenharmony_ci		csk, (csk)->state, (csk)->flags, (csk)->tid);
8598c2ecf20Sopenharmony_ci	cxgbi_sock_set_flag(csk, CTPF_ACTIVE_CLOSE_NEEDED);
8608c2ecf20Sopenharmony_ci	if (csk->state == CTP_ACTIVE_OPEN || csk->state == CTP_CLOSED)
8618c2ecf20Sopenharmony_ci		return;
8628c2ecf20Sopenharmony_ci	if (csk->saddr.sin_port)
8638c2ecf20Sopenharmony_ci		sock_put_port(csk);
8648c2ecf20Sopenharmony_ci	if (csk->dst)
8658c2ecf20Sopenharmony_ci		dst_release(csk->dst);
8668c2ecf20Sopenharmony_ci	csk->cdev->csk_release_offload_resources(csk);
8678c2ecf20Sopenharmony_ci	cxgbi_sock_set_state(csk, CTP_CLOSED);
8688c2ecf20Sopenharmony_ci	cxgbi_inform_iscsi_conn_closing(csk);
8698c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_closed);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic void need_active_close(struct cxgbi_sock *csk)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	int data_lost;
8768c2ecf20Sopenharmony_ci	int close_req = 0;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n",
8798c2ecf20Sopenharmony_ci		csk, (csk)->state, (csk)->flags, (csk)->tid);
8808c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
8818c2ecf20Sopenharmony_ci	if (csk->dst)
8828c2ecf20Sopenharmony_ci		dst_confirm(csk->dst);
8838c2ecf20Sopenharmony_ci	data_lost = skb_queue_len(&csk->receive_queue);
8848c2ecf20Sopenharmony_ci	__skb_queue_purge(&csk->receive_queue);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	if (csk->state == CTP_ACTIVE_OPEN)
8878c2ecf20Sopenharmony_ci		cxgbi_sock_set_flag(csk, CTPF_ACTIVE_CLOSE_NEEDED);
8888c2ecf20Sopenharmony_ci	else if (csk->state == CTP_ESTABLISHED) {
8898c2ecf20Sopenharmony_ci		close_req = 1;
8908c2ecf20Sopenharmony_ci		cxgbi_sock_set_state(csk, CTP_ACTIVE_CLOSE);
8918c2ecf20Sopenharmony_ci	} else if (csk->state == CTP_PASSIVE_CLOSE) {
8928c2ecf20Sopenharmony_ci		close_req = 1;
8938c2ecf20Sopenharmony_ci		cxgbi_sock_set_state(csk, CTP_CLOSE_WAIT_2);
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if (close_req) {
8978c2ecf20Sopenharmony_ci		if (!cxgbi_sock_flag(csk, CTPF_LOGOUT_RSP_RCVD) ||
8988c2ecf20Sopenharmony_ci		    data_lost)
8998c2ecf20Sopenharmony_ci			csk->cdev->csk_send_abort_req(csk);
9008c2ecf20Sopenharmony_ci		else
9018c2ecf20Sopenharmony_ci			csk->cdev->csk_send_close_req(csk);
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_civoid cxgbi_sock_fail_act_open(struct cxgbi_sock *csk, int errno)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	pr_info("csk 0x%p,%u,%lx, %pI4:%u-%pI4:%u, err %d.\n",
9108c2ecf20Sopenharmony_ci			csk, csk->state, csk->flags,
9118c2ecf20Sopenharmony_ci			&csk->saddr.sin_addr.s_addr, csk->saddr.sin_port,
9128c2ecf20Sopenharmony_ci			&csk->daddr.sin_addr.s_addr, csk->daddr.sin_port,
9138c2ecf20Sopenharmony_ci			errno);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	cxgbi_sock_set_state(csk, CTP_CONNECTING);
9168c2ecf20Sopenharmony_ci	csk->err = errno;
9178c2ecf20Sopenharmony_ci	cxgbi_sock_closed(csk);
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_fail_act_open);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_civoid cxgbi_sock_act_open_req_arp_failure(void *handle, struct sk_buff *skb)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = (struct cxgbi_sock *)skb->sk;
9248c2ecf20Sopenharmony_ci	struct module *owner = csk->cdev->owner;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n",
9278c2ecf20Sopenharmony_ci		csk, (csk)->state, (csk)->flags, (csk)->tid);
9288c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
9298c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
9308c2ecf20Sopenharmony_ci	if (csk->state == CTP_ACTIVE_OPEN)
9318c2ecf20Sopenharmony_ci		cxgbi_sock_fail_act_open(csk, -EHOSTUNREACH);
9328c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
9338c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
9348c2ecf20Sopenharmony_ci	__kfree_skb(skb);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	module_put(owner);
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_act_open_req_arp_failure);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_civoid cxgbi_sock_rcv_abort_rpl(struct cxgbi_sock *csk)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
9438c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_RCVD);
9468c2ecf20Sopenharmony_ci	if (cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) {
9478c2ecf20Sopenharmony_ci		cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_PENDING);
9488c2ecf20Sopenharmony_ci		if (cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD))
9498c2ecf20Sopenharmony_ci			pr_err("csk 0x%p,%u,0x%lx,%u,ABT_RPL_RSS.\n",
9508c2ecf20Sopenharmony_ci			       csk, csk->state, csk->flags, csk->tid);
9518c2ecf20Sopenharmony_ci		cxgbi_sock_closed(csk);
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
9558c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_rcv_abort_rpl);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_civoid cxgbi_sock_rcv_peer_close(struct cxgbi_sock *csk)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n",
9628c2ecf20Sopenharmony_ci		csk, (csk)->state, (csk)->flags, (csk)->tid);
9638c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
9648c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING))
9678c2ecf20Sopenharmony_ci		goto done;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	switch (csk->state) {
9708c2ecf20Sopenharmony_ci	case CTP_ESTABLISHED:
9718c2ecf20Sopenharmony_ci		cxgbi_sock_set_state(csk, CTP_PASSIVE_CLOSE);
9728c2ecf20Sopenharmony_ci		break;
9738c2ecf20Sopenharmony_ci	case CTP_ACTIVE_CLOSE:
9748c2ecf20Sopenharmony_ci		cxgbi_sock_set_state(csk, CTP_CLOSE_WAIT_2);
9758c2ecf20Sopenharmony_ci		break;
9768c2ecf20Sopenharmony_ci	case CTP_CLOSE_WAIT_1:
9778c2ecf20Sopenharmony_ci		cxgbi_sock_closed(csk);
9788c2ecf20Sopenharmony_ci		break;
9798c2ecf20Sopenharmony_ci	case CTP_ABORTING:
9808c2ecf20Sopenharmony_ci		break;
9818c2ecf20Sopenharmony_ci	default:
9828c2ecf20Sopenharmony_ci		pr_err("csk 0x%p,%u,0x%lx,%u, bad state.\n",
9838c2ecf20Sopenharmony_ci			csk, csk->state, csk->flags, csk->tid);
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci	cxgbi_inform_iscsi_conn_closing(csk);
9868c2ecf20Sopenharmony_cidone:
9878c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
9888c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_rcv_peer_close);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_civoid cxgbi_sock_rcv_close_conn_rpl(struct cxgbi_sock *csk, u32 snd_nxt)
9938c2ecf20Sopenharmony_ci{
9948c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n",
9958c2ecf20Sopenharmony_ci		csk, (csk)->state, (csk)->flags, (csk)->tid);
9968c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
9978c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	csk->snd_una = snd_nxt - 1;
10008c2ecf20Sopenharmony_ci	if (cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING))
10018c2ecf20Sopenharmony_ci		goto done;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	switch (csk->state) {
10048c2ecf20Sopenharmony_ci	case CTP_ACTIVE_CLOSE:
10058c2ecf20Sopenharmony_ci		cxgbi_sock_set_state(csk, CTP_CLOSE_WAIT_1);
10068c2ecf20Sopenharmony_ci		break;
10078c2ecf20Sopenharmony_ci	case CTP_CLOSE_WAIT_1:
10088c2ecf20Sopenharmony_ci	case CTP_CLOSE_WAIT_2:
10098c2ecf20Sopenharmony_ci		cxgbi_sock_closed(csk);
10108c2ecf20Sopenharmony_ci		break;
10118c2ecf20Sopenharmony_ci	case CTP_ABORTING:
10128c2ecf20Sopenharmony_ci		break;
10138c2ecf20Sopenharmony_ci	default:
10148c2ecf20Sopenharmony_ci		pr_err("csk 0x%p,%u,0x%lx,%u, bad state.\n",
10158c2ecf20Sopenharmony_ci			csk, csk->state, csk->flags, csk->tid);
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_cidone:
10188c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
10198c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_rcv_close_conn_rpl);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_civoid cxgbi_sock_rcv_wr_ack(struct cxgbi_sock *csk, unsigned int credits,
10248c2ecf20Sopenharmony_ci			   unsigned int snd_una, int seq_chk)
10258c2ecf20Sopenharmony_ci{
10268c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
10278c2ecf20Sopenharmony_ci			"csk 0x%p,%u,0x%lx,%u, cr %u,%u+%u, snd_una %u,%d.\n",
10288c2ecf20Sopenharmony_ci			csk, csk->state, csk->flags, csk->tid, credits,
10298c2ecf20Sopenharmony_ci			csk->wr_cred, csk->wr_una_cred, snd_una, seq_chk);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	csk->wr_cred += credits;
10348c2ecf20Sopenharmony_ci	if (csk->wr_una_cred > csk->wr_max_cred - csk->wr_cred)
10358c2ecf20Sopenharmony_ci		csk->wr_una_cred = csk->wr_max_cred - csk->wr_cred;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	while (credits) {
10388c2ecf20Sopenharmony_ci		struct sk_buff *p = cxgbi_sock_peek_wr(csk);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci		if (unlikely(!p)) {
10418c2ecf20Sopenharmony_ci			pr_err("csk 0x%p,%u,0x%lx,%u, cr %u,%u+%u, empty.\n",
10428c2ecf20Sopenharmony_ci				csk, csk->state, csk->flags, csk->tid, credits,
10438c2ecf20Sopenharmony_ci				csk->wr_cred, csk->wr_una_cred);
10448c2ecf20Sopenharmony_ci			break;
10458c2ecf20Sopenharmony_ci		}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci		if (unlikely(credits < p->csum)) {
10488c2ecf20Sopenharmony_ci			pr_warn("csk 0x%p,%u,0x%lx,%u, cr %u,%u+%u, < %u.\n",
10498c2ecf20Sopenharmony_ci				csk, csk->state, csk->flags, csk->tid,
10508c2ecf20Sopenharmony_ci				credits, csk->wr_cred, csk->wr_una_cred,
10518c2ecf20Sopenharmony_ci				p->csum);
10528c2ecf20Sopenharmony_ci			p->csum -= credits;
10538c2ecf20Sopenharmony_ci			break;
10548c2ecf20Sopenharmony_ci		} else {
10558c2ecf20Sopenharmony_ci			cxgbi_sock_dequeue_wr(csk);
10568c2ecf20Sopenharmony_ci			credits -= p->csum;
10578c2ecf20Sopenharmony_ci			kfree_skb(p);
10588c2ecf20Sopenharmony_ci		}
10598c2ecf20Sopenharmony_ci	}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	cxgbi_sock_check_wr_invariants(csk);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	if (seq_chk) {
10648c2ecf20Sopenharmony_ci		if (unlikely(before(snd_una, csk->snd_una))) {
10658c2ecf20Sopenharmony_ci			pr_warn("csk 0x%p,%u,0x%lx,%u, snd_una %u/%u.",
10668c2ecf20Sopenharmony_ci				csk, csk->state, csk->flags, csk->tid, snd_una,
10678c2ecf20Sopenharmony_ci				csk->snd_una);
10688c2ecf20Sopenharmony_ci			goto done;
10698c2ecf20Sopenharmony_ci		}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci		if (csk->snd_una != snd_una) {
10728c2ecf20Sopenharmony_ci			csk->snd_una = snd_una;
10738c2ecf20Sopenharmony_ci			dst_confirm(csk->dst);
10748c2ecf20Sopenharmony_ci		}
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	if (skb_queue_len(&csk->write_queue)) {
10788c2ecf20Sopenharmony_ci		if (csk->cdev->csk_push_tx_frames(csk, 0))
10798c2ecf20Sopenharmony_ci			cxgbi_conn_tx_open(csk);
10808c2ecf20Sopenharmony_ci	} else
10818c2ecf20Sopenharmony_ci		cxgbi_conn_tx_open(csk);
10828c2ecf20Sopenharmony_cidone:
10838c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_rcv_wr_ack);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic unsigned int cxgbi_sock_find_best_mtu(struct cxgbi_sock *csk,
10888c2ecf20Sopenharmony_ci					     unsigned short mtu)
10898c2ecf20Sopenharmony_ci{
10908c2ecf20Sopenharmony_ci	int i = 0;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	while (i < csk->cdev->nmtus - 1 && csk->cdev->mtus[i + 1] <= mtu)
10938c2ecf20Sopenharmony_ci		++i;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	return i;
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ciunsigned int cxgbi_sock_select_mss(struct cxgbi_sock *csk, unsigned int pmtu)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	unsigned int idx;
11018c2ecf20Sopenharmony_ci	struct dst_entry *dst = csk->dst;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	csk->advmss = dst_metric_advmss(dst);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (csk->advmss > pmtu - 40)
11068c2ecf20Sopenharmony_ci		csk->advmss = pmtu - 40;
11078c2ecf20Sopenharmony_ci	if (csk->advmss < csk->cdev->mtus[0] - 40)
11088c2ecf20Sopenharmony_ci		csk->advmss = csk->cdev->mtus[0] - 40;
11098c2ecf20Sopenharmony_ci	idx = cxgbi_sock_find_best_mtu(csk, csk->advmss + 40);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	return idx;
11128c2ecf20Sopenharmony_ci}
11138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_select_mss);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_civoid cxgbi_sock_skb_entail(struct cxgbi_sock *csk, struct sk_buff *skb)
11168c2ecf20Sopenharmony_ci{
11178c2ecf20Sopenharmony_ci	cxgbi_skcb_tcp_seq(skb) = csk->write_seq;
11188c2ecf20Sopenharmony_ci	__skb_queue_tail(&csk->write_queue, skb);
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_skb_entail);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_civoid cxgbi_sock_purge_wr_queue(struct cxgbi_sock *csk)
11238c2ecf20Sopenharmony_ci{
11248c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	while ((skb = cxgbi_sock_dequeue_wr(csk)) != NULL)
11278c2ecf20Sopenharmony_ci		kfree_skb(skb);
11288c2ecf20Sopenharmony_ci}
11298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_purge_wr_queue);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_civoid cxgbi_sock_check_wr_invariants(const struct cxgbi_sock *csk)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	int pending = cxgbi_sock_count_pending_wrs(csk);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	if (unlikely(csk->wr_cred + pending != csk->wr_max_cred))
11368c2ecf20Sopenharmony_ci		pr_err("csk 0x%p, tid %u, credit %u + %u != %u.\n",
11378c2ecf20Sopenharmony_ci			csk, csk->tid, csk->wr_cred, pending, csk->wr_max_cred);
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_sock_check_wr_invariants);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_cistatic inline void
11428c2ecf20Sopenharmony_ciscmd_get_params(struct scsi_cmnd *sc, struct scatterlist **sgl,
11438c2ecf20Sopenharmony_ci		unsigned int *sgcnt, unsigned int *dlen,
11448c2ecf20Sopenharmony_ci		unsigned int prot)
11458c2ecf20Sopenharmony_ci{
11468c2ecf20Sopenharmony_ci	struct scsi_data_buffer *sdb = prot ? scsi_prot(sc) : &sc->sdb;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	*sgl = sdb->table.sgl;
11498c2ecf20Sopenharmony_ci	*sgcnt = sdb->table.nents;
11508c2ecf20Sopenharmony_ci	*dlen = sdb->length;
11518c2ecf20Sopenharmony_ci	/* Caution: for protection sdb, sdb->length is invalid */
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_civoid cxgbi_ddp_set_one_ppod(struct cxgbi_pagepod *ppod,
11558c2ecf20Sopenharmony_ci			    struct cxgbi_task_tag_info *ttinfo,
11568c2ecf20Sopenharmony_ci			    struct scatterlist **sg_pp, unsigned int *sg_off)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct scatterlist *sg = sg_pp ? *sg_pp : NULL;
11598c2ecf20Sopenharmony_ci	unsigned int offset = sg_off ? *sg_off : 0;
11608c2ecf20Sopenharmony_ci	dma_addr_t addr = 0UL;
11618c2ecf20Sopenharmony_ci	unsigned int len = 0;
11628c2ecf20Sopenharmony_ci	int i;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	memcpy(ppod, &ttinfo->hdr, sizeof(struct cxgbi_pagepod_hdr));
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	if (sg) {
11678c2ecf20Sopenharmony_ci		addr = sg_dma_address(sg);
11688c2ecf20Sopenharmony_ci		len = sg_dma_len(sg);
11698c2ecf20Sopenharmony_ci	}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	for (i = 0; i < PPOD_PAGES_MAX; i++) {
11728c2ecf20Sopenharmony_ci		if (sg) {
11738c2ecf20Sopenharmony_ci			ppod->addr[i] = cpu_to_be64(addr + offset);
11748c2ecf20Sopenharmony_ci			offset += PAGE_SIZE;
11758c2ecf20Sopenharmony_ci			if (offset == (len + sg->offset)) {
11768c2ecf20Sopenharmony_ci				offset = 0;
11778c2ecf20Sopenharmony_ci				sg = sg_next(sg);
11788c2ecf20Sopenharmony_ci				if (sg) {
11798c2ecf20Sopenharmony_ci					addr = sg_dma_address(sg);
11808c2ecf20Sopenharmony_ci					len = sg_dma_len(sg);
11818c2ecf20Sopenharmony_ci				}
11828c2ecf20Sopenharmony_ci			}
11838c2ecf20Sopenharmony_ci		} else {
11848c2ecf20Sopenharmony_ci			ppod->addr[i] = 0ULL;
11858c2ecf20Sopenharmony_ci		}
11868c2ecf20Sopenharmony_ci	}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	/*
11898c2ecf20Sopenharmony_ci	 * the fifth address needs to be repeated in the next ppod, so do
11908c2ecf20Sopenharmony_ci	 * not move sg
11918c2ecf20Sopenharmony_ci	 */
11928c2ecf20Sopenharmony_ci	if (sg_pp) {
11938c2ecf20Sopenharmony_ci		*sg_pp = sg;
11948c2ecf20Sopenharmony_ci		*sg_off = offset;
11958c2ecf20Sopenharmony_ci	}
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	if (offset == len) {
11988c2ecf20Sopenharmony_ci		offset = 0;
11998c2ecf20Sopenharmony_ci		sg = sg_next(sg);
12008c2ecf20Sopenharmony_ci		if (sg) {
12018c2ecf20Sopenharmony_ci			addr = sg_dma_address(sg);
12028c2ecf20Sopenharmony_ci			len = sg_dma_len(sg);
12038c2ecf20Sopenharmony_ci		}
12048c2ecf20Sopenharmony_ci	}
12058c2ecf20Sopenharmony_ci	ppod->addr[i] = sg ? cpu_to_be64(addr + offset) : 0ULL;
12068c2ecf20Sopenharmony_ci}
12078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_ddp_set_one_ppod);
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci/*
12108c2ecf20Sopenharmony_ci * APIs interacting with open-iscsi libraries
12118c2ecf20Sopenharmony_ci */
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ciint cxgbi_ddp_ppm_setup(void **ppm_pp, struct cxgbi_device *cdev,
12148c2ecf20Sopenharmony_ci			struct cxgbi_tag_format *tformat,
12158c2ecf20Sopenharmony_ci			unsigned int iscsi_size, unsigned int llimit,
12168c2ecf20Sopenharmony_ci			unsigned int start, unsigned int rsvd_factor,
12178c2ecf20Sopenharmony_ci			unsigned int edram_start, unsigned int edram_size)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	int err = cxgbi_ppm_init(ppm_pp, cdev->ports[0], cdev->pdev,
12208c2ecf20Sopenharmony_ci				cdev->lldev, tformat, iscsi_size, llimit, start,
12218c2ecf20Sopenharmony_ci				rsvd_factor, edram_start, edram_size);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	if (err >= 0) {
12248c2ecf20Sopenharmony_ci		struct cxgbi_ppm *ppm = (struct cxgbi_ppm *)(*ppm_pp);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci		if (ppm->ppmax < 1024 ||
12278c2ecf20Sopenharmony_ci		    ppm->tformat.pgsz_idx_dflt >= DDP_PGIDX_MAX)
12288c2ecf20Sopenharmony_ci			cdev->flags |= CXGBI_FLAG_DDP_OFF;
12298c2ecf20Sopenharmony_ci		err = 0;
12308c2ecf20Sopenharmony_ci	} else {
12318c2ecf20Sopenharmony_ci		cdev->flags |= CXGBI_FLAG_DDP_OFF;
12328c2ecf20Sopenharmony_ci	}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	return err;
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_ddp_ppm_setup);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_cistatic int cxgbi_ddp_sgl_check(struct scatterlist *sgl, int nents)
12398c2ecf20Sopenharmony_ci{
12408c2ecf20Sopenharmony_ci	int i;
12418c2ecf20Sopenharmony_ci	int last_sgidx = nents - 1;
12428c2ecf20Sopenharmony_ci	struct scatterlist *sg = sgl;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	for (i = 0; i < nents; i++, sg = sg_next(sg)) {
12458c2ecf20Sopenharmony_ci		unsigned int len = sg->length + sg->offset;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci		if ((sg->offset & 0x3) || (i && sg->offset) ||
12488c2ecf20Sopenharmony_ci		    ((i != last_sgidx) && len != PAGE_SIZE)) {
12498c2ecf20Sopenharmony_ci			log_debug(1 << CXGBI_DBG_DDP,
12508c2ecf20Sopenharmony_ci				  "sg %u/%u, %u,%u, not aligned.\n",
12518c2ecf20Sopenharmony_ci				  i, nents, sg->offset, sg->length);
12528c2ecf20Sopenharmony_ci			goto err_out;
12538c2ecf20Sopenharmony_ci		}
12548c2ecf20Sopenharmony_ci	}
12558c2ecf20Sopenharmony_ci	return 0;
12568c2ecf20Sopenharmony_cierr_out:
12578c2ecf20Sopenharmony_ci	return -EINVAL;
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic int cxgbi_ddp_reserve(struct cxgbi_conn *cconn,
12618c2ecf20Sopenharmony_ci			     struct cxgbi_task_data *tdata, u32 sw_tag,
12628c2ecf20Sopenharmony_ci			     unsigned int xferlen)
12638c2ecf20Sopenharmony_ci{
12648c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = cconn->cep->csk;
12658c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = csk->cdev;
12668c2ecf20Sopenharmony_ci	struct cxgbi_ppm *ppm = cdev->cdev2ppm(cdev);
12678c2ecf20Sopenharmony_ci	struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
12688c2ecf20Sopenharmony_ci	struct scatterlist *sgl = ttinfo->sgl;
12698c2ecf20Sopenharmony_ci	unsigned int sgcnt = ttinfo->nents;
12708c2ecf20Sopenharmony_ci	unsigned int sg_offset = sgl->offset;
12718c2ecf20Sopenharmony_ci	int err;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	if (cdev->flags & CXGBI_FLAG_DDP_OFF) {
12748c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_DDP,
12758c2ecf20Sopenharmony_ci			  "cdev 0x%p DDP off.\n", cdev);
12768c2ecf20Sopenharmony_ci		return -EINVAL;
12778c2ecf20Sopenharmony_ci	}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	if (!ppm || xferlen < DDP_THRESHOLD || !sgcnt ||
12808c2ecf20Sopenharmony_ci	    ppm->tformat.pgsz_idx_dflt >= DDP_PGIDX_MAX) {
12818c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_DDP,
12828c2ecf20Sopenharmony_ci			  "ppm 0x%p, pgidx %u, xfer %u, sgcnt %u, NO ddp.\n",
12838c2ecf20Sopenharmony_ci			  ppm, ppm ? ppm->tformat.pgsz_idx_dflt : DDP_PGIDX_MAX,
12848c2ecf20Sopenharmony_ci			  xferlen, ttinfo->nents);
12858c2ecf20Sopenharmony_ci		return -EINVAL;
12868c2ecf20Sopenharmony_ci	}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	/* make sure the buffer is suitable for ddp */
12898c2ecf20Sopenharmony_ci	if (cxgbi_ddp_sgl_check(sgl, sgcnt) < 0)
12908c2ecf20Sopenharmony_ci		return -EINVAL;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	ttinfo->nr_pages = (xferlen + sgl->offset + (1 << PAGE_SHIFT) - 1) >>
12938c2ecf20Sopenharmony_ci			    PAGE_SHIFT;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	/*
12968c2ecf20Sopenharmony_ci	 * the ddp tag will be used for the itt in the outgoing pdu,
12978c2ecf20Sopenharmony_ci	 * the itt genrated by libiscsi is saved in the ppm and can be
12988c2ecf20Sopenharmony_ci	 * retrieved via the ddp tag
12998c2ecf20Sopenharmony_ci	 */
13008c2ecf20Sopenharmony_ci	err = cxgbi_ppm_ppods_reserve(ppm, ttinfo->nr_pages, 0, &ttinfo->idx,
13018c2ecf20Sopenharmony_ci				      &ttinfo->tag, (unsigned long)sw_tag);
13028c2ecf20Sopenharmony_ci	if (err < 0) {
13038c2ecf20Sopenharmony_ci		cconn->ddp_full++;
13048c2ecf20Sopenharmony_ci		return err;
13058c2ecf20Sopenharmony_ci	}
13068c2ecf20Sopenharmony_ci	ttinfo->npods = err;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	 /* setup dma from scsi command sgl */
13098c2ecf20Sopenharmony_ci	sgl->offset = 0;
13108c2ecf20Sopenharmony_ci	err = dma_map_sg(&ppm->pdev->dev, sgl, sgcnt, DMA_FROM_DEVICE);
13118c2ecf20Sopenharmony_ci	sgl->offset = sg_offset;
13128c2ecf20Sopenharmony_ci	if (err == 0) {
13138c2ecf20Sopenharmony_ci		pr_info("%s: 0x%x, xfer %u, sgl %u dma mapping err.\n",
13148c2ecf20Sopenharmony_ci			__func__, sw_tag, xferlen, sgcnt);
13158c2ecf20Sopenharmony_ci		goto rel_ppods;
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci	if (err != ttinfo->nr_pages) {
13188c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_DDP,
13198c2ecf20Sopenharmony_ci			  "%s: sw tag 0x%x, xfer %u, sgl %u, dma count %d.\n",
13208c2ecf20Sopenharmony_ci			  __func__, sw_tag, xferlen, sgcnt, err);
13218c2ecf20Sopenharmony_ci	}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	ttinfo->flags |= CXGBI_PPOD_INFO_FLAG_MAPPED;
13248c2ecf20Sopenharmony_ci	ttinfo->cid = csk->port_id;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	cxgbi_ppm_make_ppod_hdr(ppm, ttinfo->tag, csk->tid, sgl->offset,
13278c2ecf20Sopenharmony_ci				xferlen, &ttinfo->hdr);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	if (cdev->flags & CXGBI_FLAG_USE_PPOD_OFLDQ) {
13308c2ecf20Sopenharmony_ci		/* write ppod from xmit_pdu (of iscsi_scsi_command pdu) */
13318c2ecf20Sopenharmony_ci		ttinfo->flags |= CXGBI_PPOD_INFO_FLAG_VALID;
13328c2ecf20Sopenharmony_ci	} else {
13338c2ecf20Sopenharmony_ci		/* write ppod from control queue now */
13348c2ecf20Sopenharmony_ci		err = cdev->csk_ddp_set_map(ppm, csk, ttinfo);
13358c2ecf20Sopenharmony_ci		if (err < 0)
13368c2ecf20Sopenharmony_ci			goto rel_ppods;
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	return 0;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_cirel_ppods:
13428c2ecf20Sopenharmony_ci	cxgbi_ppm_ppod_release(ppm, ttinfo->idx);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	if (ttinfo->flags & CXGBI_PPOD_INFO_FLAG_MAPPED) {
13458c2ecf20Sopenharmony_ci		ttinfo->flags &= ~CXGBI_PPOD_INFO_FLAG_MAPPED;
13468c2ecf20Sopenharmony_ci		dma_unmap_sg(&ppm->pdev->dev, sgl, sgcnt, DMA_FROM_DEVICE);
13478c2ecf20Sopenharmony_ci	}
13488c2ecf20Sopenharmony_ci	return -EINVAL;
13498c2ecf20Sopenharmony_ci}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_cistatic void task_release_itt(struct iscsi_task *task, itt_t hdr_itt)
13528c2ecf20Sopenharmony_ci{
13538c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc = task->sc;
13548c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
13558c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
13568c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = cconn->chba->cdev;
13578c2ecf20Sopenharmony_ci	struct cxgbi_ppm *ppm = cdev->cdev2ppm(cdev);
13588c2ecf20Sopenharmony_ci	u32 tag = ntohl((__force u32)hdr_itt);
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DDP,
13618c2ecf20Sopenharmony_ci		  "cdev 0x%p, task 0x%p, release tag 0x%x.\n",
13628c2ecf20Sopenharmony_ci		  cdev, task, tag);
13638c2ecf20Sopenharmony_ci	if (sc && sc->sc_data_direction == DMA_FROM_DEVICE &&
13648c2ecf20Sopenharmony_ci	    cxgbi_ppm_is_ddp_tag(ppm, tag)) {
13658c2ecf20Sopenharmony_ci		struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
13668c2ecf20Sopenharmony_ci		struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci		if (!(cdev->flags & CXGBI_FLAG_USE_PPOD_OFLDQ))
13698c2ecf20Sopenharmony_ci			cdev->csk_ddp_clear_map(cdev, ppm, ttinfo);
13708c2ecf20Sopenharmony_ci		cxgbi_ppm_ppod_release(ppm, ttinfo->idx);
13718c2ecf20Sopenharmony_ci		dma_unmap_sg(&ppm->pdev->dev, ttinfo->sgl, ttinfo->nents,
13728c2ecf20Sopenharmony_ci			     DMA_FROM_DEVICE);
13738c2ecf20Sopenharmony_ci	}
13748c2ecf20Sopenharmony_ci}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_cistatic inline u32 cxgbi_build_sw_tag(u32 idx, u32 age)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	/* assume idx and age both are < 0x7FFF (32767) */
13798c2ecf20Sopenharmony_ci	return (idx << 16) | age;
13808c2ecf20Sopenharmony_ci}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_cistatic int task_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
13838c2ecf20Sopenharmony_ci{
13848c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc = task->sc;
13858c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = task->conn;
13868c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->session;
13878c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
13888c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
13898c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = cconn->chba->cdev;
13908c2ecf20Sopenharmony_ci	struct cxgbi_ppm *ppm = cdev->cdev2ppm(cdev);
13918c2ecf20Sopenharmony_ci	u32 sw_tag = cxgbi_build_sw_tag(task->itt, sess->age);
13928c2ecf20Sopenharmony_ci	u32 tag = 0;
13938c2ecf20Sopenharmony_ci	int err = -EINVAL;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	if (sc && sc->sc_data_direction == DMA_FROM_DEVICE) {
13968c2ecf20Sopenharmony_ci		struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
13978c2ecf20Sopenharmony_ci		struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci		scmd_get_params(sc, &ttinfo->sgl, &ttinfo->nents,
14008c2ecf20Sopenharmony_ci				&tdata->dlen, 0);
14018c2ecf20Sopenharmony_ci		err = cxgbi_ddp_reserve(cconn, tdata, sw_tag, tdata->dlen);
14028c2ecf20Sopenharmony_ci		if (!err)
14038c2ecf20Sopenharmony_ci			tag = ttinfo->tag;
14048c2ecf20Sopenharmony_ci		else
14058c2ecf20Sopenharmony_ci			 log_debug(1 << CXGBI_DBG_DDP,
14068c2ecf20Sopenharmony_ci				   "csk 0x%p, R task 0x%p, %u,%u, no ddp.\n",
14078c2ecf20Sopenharmony_ci				   cconn->cep->csk, task, tdata->dlen,
14088c2ecf20Sopenharmony_ci				   ttinfo->nents);
14098c2ecf20Sopenharmony_ci	}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	if (err < 0) {
14128c2ecf20Sopenharmony_ci		err = cxgbi_ppm_make_non_ddp_tag(ppm, sw_tag, &tag);
14138c2ecf20Sopenharmony_ci		if (err < 0)
14148c2ecf20Sopenharmony_ci			return err;
14158c2ecf20Sopenharmony_ci	}
14168c2ecf20Sopenharmony_ci	/*  the itt need to sent in big-endian order */
14178c2ecf20Sopenharmony_ci	*hdr_itt = (__force itt_t)htonl(tag);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DDP,
14208c2ecf20Sopenharmony_ci		  "cdev 0x%p, task 0x%p, 0x%x(0x%x,0x%x)->0x%x/0x%x.\n",
14218c2ecf20Sopenharmony_ci		  cdev, task, sw_tag, task->itt, sess->age, tag, *hdr_itt);
14228c2ecf20Sopenharmony_ci	return 0;
14238c2ecf20Sopenharmony_ci}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_civoid cxgbi_parse_pdu_itt(struct iscsi_conn *conn, itt_t itt, int *idx, int *age)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
14288c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
14298c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = cconn->chba->cdev;
14308c2ecf20Sopenharmony_ci	struct cxgbi_ppm *ppm = cdev->cdev2ppm(cdev);
14318c2ecf20Sopenharmony_ci	u32 tag = ntohl((__force u32)itt);
14328c2ecf20Sopenharmony_ci	u32 sw_bits;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	if (ppm) {
14358c2ecf20Sopenharmony_ci		if (cxgbi_ppm_is_ddp_tag(ppm, tag))
14368c2ecf20Sopenharmony_ci			sw_bits = cxgbi_ppm_get_tag_caller_data(ppm, tag);
14378c2ecf20Sopenharmony_ci		else
14388c2ecf20Sopenharmony_ci			sw_bits = cxgbi_ppm_decode_non_ddp_tag(ppm, tag);
14398c2ecf20Sopenharmony_ci	} else {
14408c2ecf20Sopenharmony_ci		sw_bits = tag;
14418c2ecf20Sopenharmony_ci	}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	cxgbi_decode_sw_tag(sw_bits, idx, age);
14448c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_DDP,
14458c2ecf20Sopenharmony_ci		  "cdev 0x%p, tag 0x%x/0x%x, -> 0x%x(0x%x,0x%x).\n",
14468c2ecf20Sopenharmony_ci		  cdev, tag, itt, sw_bits, idx ? *idx : 0xFFFFF,
14478c2ecf20Sopenharmony_ci		  age ? *age : 0xFF);
14488c2ecf20Sopenharmony_ci}
14498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_parse_pdu_itt);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_civoid cxgbi_conn_tx_open(struct cxgbi_sock *csk)
14528c2ecf20Sopenharmony_ci{
14538c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = csk->user_data;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	if (conn) {
14568c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_SOCK,
14578c2ecf20Sopenharmony_ci			"csk 0x%p, cid %d.\n", csk, conn->id);
14588c2ecf20Sopenharmony_ci		iscsi_conn_queue_work(conn);
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci}
14618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_conn_tx_open);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci/*
14648c2ecf20Sopenharmony_ci * pdu receive, interact with libiscsi_tcp
14658c2ecf20Sopenharmony_ci */
14668c2ecf20Sopenharmony_cistatic inline int read_pdu_skb(struct iscsi_conn *conn,
14678c2ecf20Sopenharmony_ci			       struct sk_buff *skb,
14688c2ecf20Sopenharmony_ci			       unsigned int offset,
14698c2ecf20Sopenharmony_ci			       int offloaded)
14708c2ecf20Sopenharmony_ci{
14718c2ecf20Sopenharmony_ci	int status = 0;
14728c2ecf20Sopenharmony_ci	int bytes_read;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	bytes_read = iscsi_tcp_recv_skb(conn, skb, offset, offloaded, &status);
14758c2ecf20Sopenharmony_ci	switch (status) {
14768c2ecf20Sopenharmony_ci	case ISCSI_TCP_CONN_ERR:
14778c2ecf20Sopenharmony_ci		pr_info("skb 0x%p, off %u, %d, TCP_ERR.\n",
14788c2ecf20Sopenharmony_ci			  skb, offset, offloaded);
14798c2ecf20Sopenharmony_ci		return -EIO;
14808c2ecf20Sopenharmony_ci	case ISCSI_TCP_SUSPENDED:
14818c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_RX,
14828c2ecf20Sopenharmony_ci			"skb 0x%p, off %u, %d, TCP_SUSPEND, rc %d.\n",
14838c2ecf20Sopenharmony_ci			skb, offset, offloaded, bytes_read);
14848c2ecf20Sopenharmony_ci		/* no transfer - just have caller flush queue */
14858c2ecf20Sopenharmony_ci		return bytes_read;
14868c2ecf20Sopenharmony_ci	case ISCSI_TCP_SKB_DONE:
14878c2ecf20Sopenharmony_ci		pr_info("skb 0x%p, off %u, %d, TCP_SKB_DONE.\n",
14888c2ecf20Sopenharmony_ci			skb, offset, offloaded);
14898c2ecf20Sopenharmony_ci		/*
14908c2ecf20Sopenharmony_ci		 * pdus should always fit in the skb and we should get
14918c2ecf20Sopenharmony_ci		 * segment done notifcation.
14928c2ecf20Sopenharmony_ci		 */
14938c2ecf20Sopenharmony_ci		iscsi_conn_printk(KERN_ERR, conn, "Invalid pdu or skb.");
14948c2ecf20Sopenharmony_ci		return -EFAULT;
14958c2ecf20Sopenharmony_ci	case ISCSI_TCP_SEGMENT_DONE:
14968c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_RX,
14978c2ecf20Sopenharmony_ci			"skb 0x%p, off %u, %d, TCP_SEG_DONE, rc %d.\n",
14988c2ecf20Sopenharmony_ci			skb, offset, offloaded, bytes_read);
14998c2ecf20Sopenharmony_ci		return bytes_read;
15008c2ecf20Sopenharmony_ci	default:
15018c2ecf20Sopenharmony_ci		pr_info("skb 0x%p, off %u, %d, invalid status %d.\n",
15028c2ecf20Sopenharmony_ci			skb, offset, offloaded, status);
15038c2ecf20Sopenharmony_ci		return -EINVAL;
15048c2ecf20Sopenharmony_ci	}
15058c2ecf20Sopenharmony_ci}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_cistatic int
15088c2ecf20Sopenharmony_ciskb_read_pdu_bhs(struct cxgbi_sock *csk, struct iscsi_conn *conn,
15098c2ecf20Sopenharmony_ci		 struct sk_buff *skb)
15108c2ecf20Sopenharmony_ci{
15118c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
15128c2ecf20Sopenharmony_ci	int err;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_PDU_RX,
15158c2ecf20Sopenharmony_ci		"conn 0x%p, skb 0x%p, len %u, flag 0x%lx.\n",
15168c2ecf20Sopenharmony_ci		conn, skb, skb->len, cxgbi_skcb_flags(skb));
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	if (!iscsi_tcp_recv_segment_is_hdr(tcp_conn)) {
15198c2ecf20Sopenharmony_ci		pr_info("conn 0x%p, skb 0x%p, not hdr.\n", conn, skb);
15208c2ecf20Sopenharmony_ci		iscsi_conn_failure(conn, ISCSI_ERR_PROTO);
15218c2ecf20Sopenharmony_ci		return -EIO;
15228c2ecf20Sopenharmony_ci	}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	if (conn->hdrdgst_en &&
15258c2ecf20Sopenharmony_ci	    cxgbi_skcb_test_flag(skb, SKCBF_RX_HCRC_ERR)) {
15268c2ecf20Sopenharmony_ci		pr_info("conn 0x%p, skb 0x%p, hcrc.\n", conn, skb);
15278c2ecf20Sopenharmony_ci		iscsi_conn_failure(conn, ISCSI_ERR_HDR_DGST);
15288c2ecf20Sopenharmony_ci		return -EIO;
15298c2ecf20Sopenharmony_ci	}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	if (cxgbi_skcb_test_flag(skb, SKCBF_RX_ISCSI_COMPL) &&
15328c2ecf20Sopenharmony_ci	    cxgbi_skcb_test_flag(skb, SKCBF_RX_DATA_DDPD)) {
15338c2ecf20Sopenharmony_ci		/* If completion flag is set and data is directly
15348c2ecf20Sopenharmony_ci		 * placed in to the host memory then update
15358c2ecf20Sopenharmony_ci		 * task->exp_datasn to the datasn in completion
15368c2ecf20Sopenharmony_ci		 * iSCSI hdr as T6 adapter generates completion only
15378c2ecf20Sopenharmony_ci		 * for the last pdu of a sequence.
15388c2ecf20Sopenharmony_ci		 */
15398c2ecf20Sopenharmony_ci		itt_t itt = ((struct iscsi_data *)skb->data)->itt;
15408c2ecf20Sopenharmony_ci		struct iscsi_task *task = iscsi_itt_to_ctask(conn, itt);
15418c2ecf20Sopenharmony_ci		u32 data_sn = be32_to_cpu(((struct iscsi_data *)
15428c2ecf20Sopenharmony_ci							skb->data)->datasn);
15438c2ecf20Sopenharmony_ci		if (task && task->sc) {
15448c2ecf20Sopenharmony_ci			struct iscsi_tcp_task *tcp_task = task->dd_data;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci			tcp_task->exp_datasn = data_sn;
15478c2ecf20Sopenharmony_ci		}
15488c2ecf20Sopenharmony_ci	}
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	err = read_pdu_skb(conn, skb, 0, 0);
15518c2ecf20Sopenharmony_ci	if (likely(err >= 0)) {
15528c2ecf20Sopenharmony_ci		struct iscsi_hdr *hdr = (struct iscsi_hdr *)skb->data;
15538c2ecf20Sopenharmony_ci		u8 opcode = hdr->opcode & ISCSI_OPCODE_MASK;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci		if (unlikely(opcode == ISCSI_OP_LOGOUT_RSP))
15568c2ecf20Sopenharmony_ci			cxgbi_sock_set_flag(csk, CTPF_LOGOUT_RSP_RCVD);
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	return err;
15608c2ecf20Sopenharmony_ci}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_cistatic int skb_read_pdu_data(struct iscsi_conn *conn, struct sk_buff *lskb,
15638c2ecf20Sopenharmony_ci			     struct sk_buff *skb, unsigned int offset)
15648c2ecf20Sopenharmony_ci{
15658c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
15668c2ecf20Sopenharmony_ci	bool offloaded = 0;
15678c2ecf20Sopenharmony_ci	int opcode = tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_PDU_RX,
15708c2ecf20Sopenharmony_ci		"conn 0x%p, skb 0x%p, len %u, flag 0x%lx.\n",
15718c2ecf20Sopenharmony_ci		conn, skb, skb->len, cxgbi_skcb_flags(skb));
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	if (conn->datadgst_en &&
15748c2ecf20Sopenharmony_ci	    cxgbi_skcb_test_flag(lskb, SKCBF_RX_DCRC_ERR)) {
15758c2ecf20Sopenharmony_ci		pr_info("conn 0x%p, skb 0x%p, dcrc 0x%lx.\n",
15768c2ecf20Sopenharmony_ci			conn, lskb, cxgbi_skcb_flags(lskb));
15778c2ecf20Sopenharmony_ci		iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
15788c2ecf20Sopenharmony_ci		return -EIO;
15798c2ecf20Sopenharmony_ci	}
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	if (iscsi_tcp_recv_segment_is_hdr(tcp_conn))
15828c2ecf20Sopenharmony_ci		return 0;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	/* coalesced, add header digest length */
15858c2ecf20Sopenharmony_ci	if (lskb == skb && conn->hdrdgst_en)
15868c2ecf20Sopenharmony_ci		offset += ISCSI_DIGEST_SIZE;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	if (cxgbi_skcb_test_flag(lskb, SKCBF_RX_DATA_DDPD))
15898c2ecf20Sopenharmony_ci		offloaded = 1;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	if (opcode == ISCSI_OP_SCSI_DATA_IN)
15928c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_RX,
15938c2ecf20Sopenharmony_ci			"skb 0x%p, op 0x%x, itt 0x%x, %u %s ddp'ed.\n",
15948c2ecf20Sopenharmony_ci			skb, opcode, ntohl(tcp_conn->in.hdr->itt),
15958c2ecf20Sopenharmony_ci			tcp_conn->in.datalen, offloaded ? "is" : "not");
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	return read_pdu_skb(conn, skb, offset, offloaded);
15988c2ecf20Sopenharmony_ci}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_cistatic void csk_return_rx_credits(struct cxgbi_sock *csk, int copied)
16018c2ecf20Sopenharmony_ci{
16028c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = csk->cdev;
16038c2ecf20Sopenharmony_ci	int must_send;
16048c2ecf20Sopenharmony_ci	u32 credits;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_PDU_RX,
16078c2ecf20Sopenharmony_ci		"csk 0x%p,%u,0x%lx,%u, seq %u, wup %u, thre %u, %u.\n",
16088c2ecf20Sopenharmony_ci		csk, csk->state, csk->flags, csk->tid, csk->copied_seq,
16098c2ecf20Sopenharmony_ci		csk->rcv_wup, cdev->rx_credit_thres,
16108c2ecf20Sopenharmony_ci		csk->rcv_win);
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	if (!cdev->rx_credit_thres)
16138c2ecf20Sopenharmony_ci		return;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	if (csk->state != CTP_ESTABLISHED)
16168c2ecf20Sopenharmony_ci		return;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	credits = csk->copied_seq - csk->rcv_wup;
16198c2ecf20Sopenharmony_ci	if (unlikely(!credits))
16208c2ecf20Sopenharmony_ci		return;
16218c2ecf20Sopenharmony_ci	must_send = credits + 16384 >= csk->rcv_win;
16228c2ecf20Sopenharmony_ci	if (must_send || credits >= cdev->rx_credit_thres)
16238c2ecf20Sopenharmony_ci		csk->rcv_wup += cdev->csk_send_rx_credits(csk, credits);
16248c2ecf20Sopenharmony_ci}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_civoid cxgbi_conn_pdu_ready(struct cxgbi_sock *csk)
16278c2ecf20Sopenharmony_ci{
16288c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = csk->cdev;
16298c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = csk->user_data;
16308c2ecf20Sopenharmony_ci	struct sk_buff *skb;
16318c2ecf20Sopenharmony_ci	unsigned int read = 0;
16328c2ecf20Sopenharmony_ci	int err = 0;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_PDU_RX,
16358c2ecf20Sopenharmony_ci		"csk 0x%p, conn 0x%p.\n", csk, conn);
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	if (unlikely(!conn || conn->suspend_rx)) {
16388c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_RX,
16398c2ecf20Sopenharmony_ci			"csk 0x%p, conn 0x%p, id %d, suspend_rx %lu!\n",
16408c2ecf20Sopenharmony_ci			csk, conn, conn ? conn->id : 0xFF,
16418c2ecf20Sopenharmony_ci			conn ? conn->suspend_rx : 0xFF);
16428c2ecf20Sopenharmony_ci		return;
16438c2ecf20Sopenharmony_ci	}
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	while (!err) {
16468c2ecf20Sopenharmony_ci		skb = skb_peek(&csk->receive_queue);
16478c2ecf20Sopenharmony_ci		if (!skb ||
16488c2ecf20Sopenharmony_ci		    !(cxgbi_skcb_test_flag(skb, SKCBF_RX_STATUS))) {
16498c2ecf20Sopenharmony_ci			if (skb)
16508c2ecf20Sopenharmony_ci				log_debug(1 << CXGBI_DBG_PDU_RX,
16518c2ecf20Sopenharmony_ci					"skb 0x%p, NOT ready 0x%lx.\n",
16528c2ecf20Sopenharmony_ci					skb, cxgbi_skcb_flags(skb));
16538c2ecf20Sopenharmony_ci			break;
16548c2ecf20Sopenharmony_ci		}
16558c2ecf20Sopenharmony_ci		__skb_unlink(skb, &csk->receive_queue);
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci		read += cxgbi_skcb_rx_pdulen(skb);
16588c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_RX,
16598c2ecf20Sopenharmony_ci			"csk 0x%p, skb 0x%p,%u,f 0x%lx, pdu len %u.\n",
16608c2ecf20Sopenharmony_ci			csk, skb, skb->len, cxgbi_skcb_flags(skb),
16618c2ecf20Sopenharmony_ci			cxgbi_skcb_rx_pdulen(skb));
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci		if (cxgbi_skcb_test_flag(skb, SKCBF_RX_COALESCED)) {
16648c2ecf20Sopenharmony_ci			err = skb_read_pdu_bhs(csk, conn, skb);
16658c2ecf20Sopenharmony_ci			if (err < 0) {
16668c2ecf20Sopenharmony_ci				pr_err("coalesced bhs, csk 0x%p, skb 0x%p,%u, "
16678c2ecf20Sopenharmony_ci					"f 0x%lx, plen %u.\n",
16688c2ecf20Sopenharmony_ci					csk, skb, skb->len,
16698c2ecf20Sopenharmony_ci					cxgbi_skcb_flags(skb),
16708c2ecf20Sopenharmony_ci					cxgbi_skcb_rx_pdulen(skb));
16718c2ecf20Sopenharmony_ci				goto skb_done;
16728c2ecf20Sopenharmony_ci			}
16738c2ecf20Sopenharmony_ci			err = skb_read_pdu_data(conn, skb, skb,
16748c2ecf20Sopenharmony_ci						err + cdev->skb_rx_extra);
16758c2ecf20Sopenharmony_ci			if (err < 0)
16768c2ecf20Sopenharmony_ci				pr_err("coalesced data, csk 0x%p, skb 0x%p,%u, "
16778c2ecf20Sopenharmony_ci					"f 0x%lx, plen %u.\n",
16788c2ecf20Sopenharmony_ci					csk, skb, skb->len,
16798c2ecf20Sopenharmony_ci					cxgbi_skcb_flags(skb),
16808c2ecf20Sopenharmony_ci					cxgbi_skcb_rx_pdulen(skb));
16818c2ecf20Sopenharmony_ci		} else {
16828c2ecf20Sopenharmony_ci			err = skb_read_pdu_bhs(csk, conn, skb);
16838c2ecf20Sopenharmony_ci			if (err < 0) {
16848c2ecf20Sopenharmony_ci				pr_err("bhs, csk 0x%p, skb 0x%p,%u, "
16858c2ecf20Sopenharmony_ci					"f 0x%lx, plen %u.\n",
16868c2ecf20Sopenharmony_ci					csk, skb, skb->len,
16878c2ecf20Sopenharmony_ci					cxgbi_skcb_flags(skb),
16888c2ecf20Sopenharmony_ci					cxgbi_skcb_rx_pdulen(skb));
16898c2ecf20Sopenharmony_ci				goto skb_done;
16908c2ecf20Sopenharmony_ci			}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci			if (cxgbi_skcb_test_flag(skb, SKCBF_RX_DATA)) {
16938c2ecf20Sopenharmony_ci				struct sk_buff *dskb;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci				dskb = skb_peek(&csk->receive_queue);
16968c2ecf20Sopenharmony_ci				if (!dskb) {
16978c2ecf20Sopenharmony_ci					pr_err("csk 0x%p, skb 0x%p,%u, f 0x%lx,"
16988c2ecf20Sopenharmony_ci						" plen %u, NO data.\n",
16998c2ecf20Sopenharmony_ci						csk, skb, skb->len,
17008c2ecf20Sopenharmony_ci						cxgbi_skcb_flags(skb),
17018c2ecf20Sopenharmony_ci						cxgbi_skcb_rx_pdulen(skb));
17028c2ecf20Sopenharmony_ci					err = -EIO;
17038c2ecf20Sopenharmony_ci					goto skb_done;
17048c2ecf20Sopenharmony_ci				}
17058c2ecf20Sopenharmony_ci				__skb_unlink(dskb, &csk->receive_queue);
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci				err = skb_read_pdu_data(conn, skb, dskb, 0);
17088c2ecf20Sopenharmony_ci				if (err < 0)
17098c2ecf20Sopenharmony_ci					pr_err("data, csk 0x%p, skb 0x%p,%u, "
17108c2ecf20Sopenharmony_ci						"f 0x%lx, plen %u, dskb 0x%p,"
17118c2ecf20Sopenharmony_ci						"%u.\n",
17128c2ecf20Sopenharmony_ci						csk, skb, skb->len,
17138c2ecf20Sopenharmony_ci						cxgbi_skcb_flags(skb),
17148c2ecf20Sopenharmony_ci						cxgbi_skcb_rx_pdulen(skb),
17158c2ecf20Sopenharmony_ci						dskb, dskb->len);
17168c2ecf20Sopenharmony_ci				__kfree_skb(dskb);
17178c2ecf20Sopenharmony_ci			} else
17188c2ecf20Sopenharmony_ci				err = skb_read_pdu_data(conn, skb, skb, 0);
17198c2ecf20Sopenharmony_ci		}
17208c2ecf20Sopenharmony_ciskb_done:
17218c2ecf20Sopenharmony_ci		__kfree_skb(skb);
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci		if (err < 0)
17248c2ecf20Sopenharmony_ci			break;
17258c2ecf20Sopenharmony_ci	}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_PDU_RX, "csk 0x%p, read %u.\n", csk, read);
17288c2ecf20Sopenharmony_ci	if (read) {
17298c2ecf20Sopenharmony_ci		csk->copied_seq += read;
17308c2ecf20Sopenharmony_ci		csk_return_rx_credits(csk, read);
17318c2ecf20Sopenharmony_ci		conn->rxdata_octets += read;
17328c2ecf20Sopenharmony_ci	}
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	if (err < 0) {
17358c2ecf20Sopenharmony_ci		pr_info("csk 0x%p, 0x%p, rx failed %d, read %u.\n",
17368c2ecf20Sopenharmony_ci			csk, conn, err, read);
17378c2ecf20Sopenharmony_ci		iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
17388c2ecf20Sopenharmony_ci	}
17398c2ecf20Sopenharmony_ci}
17408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_conn_pdu_ready);
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_cistatic int sgl_seek_offset(struct scatterlist *sgl, unsigned int sgcnt,
17438c2ecf20Sopenharmony_ci				unsigned int offset, unsigned int *off,
17448c2ecf20Sopenharmony_ci				struct scatterlist **sgp)
17458c2ecf20Sopenharmony_ci{
17468c2ecf20Sopenharmony_ci	int i;
17478c2ecf20Sopenharmony_ci	struct scatterlist *sg;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	for_each_sg(sgl, sg, sgcnt, i) {
17508c2ecf20Sopenharmony_ci		if (offset < sg->length) {
17518c2ecf20Sopenharmony_ci			*off = offset;
17528c2ecf20Sopenharmony_ci			*sgp = sg;
17538c2ecf20Sopenharmony_ci			return 0;
17548c2ecf20Sopenharmony_ci		}
17558c2ecf20Sopenharmony_ci		offset -= sg->length;
17568c2ecf20Sopenharmony_ci	}
17578c2ecf20Sopenharmony_ci	return -EFAULT;
17588c2ecf20Sopenharmony_ci}
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_cistatic int
17618c2ecf20Sopenharmony_cisgl_read_to_frags(struct scatterlist *sg, unsigned int sgoffset,
17628c2ecf20Sopenharmony_ci		  unsigned int dlen, struct page_frag *frags,
17638c2ecf20Sopenharmony_ci		  int frag_max, u32 *dlimit)
17648c2ecf20Sopenharmony_ci{
17658c2ecf20Sopenharmony_ci	unsigned int datalen = dlen;
17668c2ecf20Sopenharmony_ci	unsigned int sglen = sg->length - sgoffset;
17678c2ecf20Sopenharmony_ci	struct page *page = sg_page(sg);
17688c2ecf20Sopenharmony_ci	int i;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	i = 0;
17718c2ecf20Sopenharmony_ci	do {
17728c2ecf20Sopenharmony_ci		unsigned int copy;
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci		if (!sglen) {
17758c2ecf20Sopenharmony_ci			sg = sg_next(sg);
17768c2ecf20Sopenharmony_ci			if (!sg) {
17778c2ecf20Sopenharmony_ci				pr_warn("sg %d NULL, len %u/%u.\n",
17788c2ecf20Sopenharmony_ci					i, datalen, dlen);
17798c2ecf20Sopenharmony_ci				return -EINVAL;
17808c2ecf20Sopenharmony_ci			}
17818c2ecf20Sopenharmony_ci			sgoffset = 0;
17828c2ecf20Sopenharmony_ci			sglen = sg->length;
17838c2ecf20Sopenharmony_ci			page = sg_page(sg);
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci		}
17868c2ecf20Sopenharmony_ci		copy = min(datalen, sglen);
17878c2ecf20Sopenharmony_ci		if (i && page == frags[i - 1].page &&
17888c2ecf20Sopenharmony_ci		    sgoffset + sg->offset ==
17898c2ecf20Sopenharmony_ci			frags[i - 1].offset + frags[i - 1].size) {
17908c2ecf20Sopenharmony_ci			frags[i - 1].size += copy;
17918c2ecf20Sopenharmony_ci		} else {
17928c2ecf20Sopenharmony_ci			if (i >= frag_max) {
17938c2ecf20Sopenharmony_ci				pr_warn("too many pages %u, dlen %u.\n",
17948c2ecf20Sopenharmony_ci					frag_max, dlen);
17958c2ecf20Sopenharmony_ci				*dlimit = dlen - datalen;
17968c2ecf20Sopenharmony_ci				return -EINVAL;
17978c2ecf20Sopenharmony_ci			}
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci			frags[i].page = page;
18008c2ecf20Sopenharmony_ci			frags[i].offset = sg->offset + sgoffset;
18018c2ecf20Sopenharmony_ci			frags[i].size = copy;
18028c2ecf20Sopenharmony_ci			i++;
18038c2ecf20Sopenharmony_ci		}
18048c2ecf20Sopenharmony_ci		datalen -= copy;
18058c2ecf20Sopenharmony_ci		sgoffset += copy;
18068c2ecf20Sopenharmony_ci		sglen -= copy;
18078c2ecf20Sopenharmony_ci	} while (datalen);
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	return i;
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_cistatic void cxgbi_task_data_sgl_check(struct iscsi_task *task)
18138c2ecf20Sopenharmony_ci{
18148c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc = task->sc;
18158c2ecf20Sopenharmony_ci	struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
18168c2ecf20Sopenharmony_ci	struct scatterlist *sg, *sgl = NULL;
18178c2ecf20Sopenharmony_ci	u32 sgcnt = 0;
18188c2ecf20Sopenharmony_ci	int i;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	tdata->flags = CXGBI_TASK_SGL_CHECKED;
18218c2ecf20Sopenharmony_ci	if (!sc)
18228c2ecf20Sopenharmony_ci		return;
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	scmd_get_params(sc, &sgl, &sgcnt, &tdata->dlen, 0);
18258c2ecf20Sopenharmony_ci	if (!sgl || !sgcnt) {
18268c2ecf20Sopenharmony_ci		tdata->flags |= CXGBI_TASK_SGL_COPY;
18278c2ecf20Sopenharmony_ci		return;
18288c2ecf20Sopenharmony_ci	}
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	for_each_sg(sgl, sg, sgcnt, i) {
18318c2ecf20Sopenharmony_ci		if (page_count(sg_page(sg)) < 1) {
18328c2ecf20Sopenharmony_ci			tdata->flags |= CXGBI_TASK_SGL_COPY;
18338c2ecf20Sopenharmony_ci			return;
18348c2ecf20Sopenharmony_ci		}
18358c2ecf20Sopenharmony_ci	}
18368c2ecf20Sopenharmony_ci}
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_cistatic int
18398c2ecf20Sopenharmony_cicxgbi_task_data_sgl_read(struct iscsi_task *task, u32 offset, u32 count,
18408c2ecf20Sopenharmony_ci			 u32 *dlimit)
18418c2ecf20Sopenharmony_ci{
18428c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc = task->sc;
18438c2ecf20Sopenharmony_ci	struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
18448c2ecf20Sopenharmony_ci	struct scatterlist *sgl = NULL;
18458c2ecf20Sopenharmony_ci	struct scatterlist *sg;
18468c2ecf20Sopenharmony_ci	u32 dlen = 0;
18478c2ecf20Sopenharmony_ci	u32 sgcnt;
18488c2ecf20Sopenharmony_ci	int err;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	if (!sc)
18518c2ecf20Sopenharmony_ci		return 0;
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	scmd_get_params(sc, &sgl, &sgcnt, &dlen, 0);
18548c2ecf20Sopenharmony_ci	if (!sgl || !sgcnt)
18558c2ecf20Sopenharmony_ci		return 0;
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	err = sgl_seek_offset(sgl, sgcnt, offset, &tdata->sgoffset, &sg);
18588c2ecf20Sopenharmony_ci	if (err < 0) {
18598c2ecf20Sopenharmony_ci		pr_warn("tpdu max, sgl %u, bad offset %u/%u.\n",
18608c2ecf20Sopenharmony_ci			sgcnt, offset, tdata->dlen);
18618c2ecf20Sopenharmony_ci		return err;
18628c2ecf20Sopenharmony_ci	}
18638c2ecf20Sopenharmony_ci	err = sgl_read_to_frags(sg, tdata->sgoffset, count,
18648c2ecf20Sopenharmony_ci				tdata->frags, MAX_SKB_FRAGS, dlimit);
18658c2ecf20Sopenharmony_ci	if (err < 0) {
18668c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_ISCSI,
18678c2ecf20Sopenharmony_ci			  "sgl max limit, sgl %u, offset %u, %u/%u, dlimit %u.\n",
18688c2ecf20Sopenharmony_ci			  sgcnt, offset, count, tdata->dlen, *dlimit);
18698c2ecf20Sopenharmony_ci		return err;
18708c2ecf20Sopenharmony_ci	}
18718c2ecf20Sopenharmony_ci	tdata->offset = offset;
18728c2ecf20Sopenharmony_ci	tdata->count = count;
18738c2ecf20Sopenharmony_ci	tdata->nr_frags = err;
18748c2ecf20Sopenharmony_ci	tdata->total_count = count;
18758c2ecf20Sopenharmony_ci	tdata->total_offset = offset;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
18788c2ecf20Sopenharmony_ci		  "%s: offset %u, count %u,\n"
18798c2ecf20Sopenharmony_ci		  "err %u, total_count %u, total_offset %u\n",
18808c2ecf20Sopenharmony_ci		  __func__, offset, count, err,  tdata->total_count, tdata->total_offset);
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	return 0;
18838c2ecf20Sopenharmony_ci}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ciint cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 op)
18868c2ecf20Sopenharmony_ci{
18878c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = task->conn;
18888c2ecf20Sopenharmony_ci	struct iscsi_session *session = task->conn->session;
18898c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
18908c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
18918c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = cconn->chba->cdev;
18928c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = cconn->cep ? cconn->cep->csk : NULL;
18938c2ecf20Sopenharmony_ci	struct iscsi_tcp_task *tcp_task = task->dd_data;
18948c2ecf20Sopenharmony_ci	struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
18958c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc = task->sc;
18968c2ecf20Sopenharmony_ci	u32 headroom = SKB_TX_ISCSI_PDU_HEADER_MAX;
18978c2ecf20Sopenharmony_ci	u32 max_txdata_len = conn->max_xmit_dlength;
18988c2ecf20Sopenharmony_ci	u32 iso_tx_rsvd = 0, local_iso_info = 0;
18998c2ecf20Sopenharmony_ci	u32 last_tdata_offset, last_tdata_count;
19008c2ecf20Sopenharmony_ci	int err = 0;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	if (!tcp_task) {
19038c2ecf20Sopenharmony_ci		pr_err("task 0x%p, tcp_task 0x%p, tdata 0x%p.\n",
19048c2ecf20Sopenharmony_ci		       task, tcp_task, tdata);
19058c2ecf20Sopenharmony_ci		return -ENOMEM;
19068c2ecf20Sopenharmony_ci	}
19078c2ecf20Sopenharmony_ci	if (!csk) {
19088c2ecf20Sopenharmony_ci		pr_err("task 0x%p, csk gone.\n", task);
19098c2ecf20Sopenharmony_ci		return -EPIPE;
19108c2ecf20Sopenharmony_ci	}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	op &= ISCSI_OPCODE_MASK;
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	tcp_task->dd_data = tdata;
19158c2ecf20Sopenharmony_ci	task->hdr = NULL;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	last_tdata_count = tdata->count;
19188c2ecf20Sopenharmony_ci	last_tdata_offset = tdata->offset;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	if ((op == ISCSI_OP_SCSI_DATA_OUT) ||
19218c2ecf20Sopenharmony_ci	    ((op == ISCSI_OP_SCSI_CMD) &&
19228c2ecf20Sopenharmony_ci	     (sc->sc_data_direction == DMA_TO_DEVICE))) {
19238c2ecf20Sopenharmony_ci		u32 remaining_data_tosend, dlimit = 0;
19248c2ecf20Sopenharmony_ci		u32 max_pdu_size, max_num_pdu, num_pdu;
19258c2ecf20Sopenharmony_ci		u32 count;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci		/* Preserve conn->max_xmit_dlength because it can get updated to
19288c2ecf20Sopenharmony_ci		 * ISO data size.
19298c2ecf20Sopenharmony_ci		 */
19308c2ecf20Sopenharmony_ci		if (task->state == ISCSI_TASK_PENDING)
19318c2ecf20Sopenharmony_ci			tdata->max_xmit_dlength = conn->max_xmit_dlength;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci		if (!tdata->offset)
19348c2ecf20Sopenharmony_ci			cxgbi_task_data_sgl_check(task);
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci		remaining_data_tosend =
19378c2ecf20Sopenharmony_ci			tdata->dlen - tdata->offset - tdata->count;
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cirecalculate_sgl:
19408c2ecf20Sopenharmony_ci		max_txdata_len = tdata->max_xmit_dlength;
19418c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
19428c2ecf20Sopenharmony_ci			  "tdata->dlen %u, remaining to send %u "
19438c2ecf20Sopenharmony_ci			  "conn->max_xmit_dlength %u, "
19448c2ecf20Sopenharmony_ci			  "tdata->max_xmit_dlength %u\n",
19458c2ecf20Sopenharmony_ci			  tdata->dlen, remaining_data_tosend,
19468c2ecf20Sopenharmony_ci			  conn->max_xmit_dlength, tdata->max_xmit_dlength);
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci		if (cdev->skb_iso_txhdr && !csk->disable_iso &&
19498c2ecf20Sopenharmony_ci		    (remaining_data_tosend > tdata->max_xmit_dlength) &&
19508c2ecf20Sopenharmony_ci		    !(remaining_data_tosend % 4)) {
19518c2ecf20Sopenharmony_ci			u32 max_iso_data;
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci			if ((op == ISCSI_OP_SCSI_CMD) &&
19548c2ecf20Sopenharmony_ci			    session->initial_r2t_en)
19558c2ecf20Sopenharmony_ci				goto no_iso;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci			max_pdu_size = tdata->max_xmit_dlength +
19588c2ecf20Sopenharmony_ci				       ISCSI_PDU_NONPAYLOAD_LEN;
19598c2ecf20Sopenharmony_ci			max_iso_data = rounddown(CXGBI_MAX_ISO_DATA_IN_SKB,
19608c2ecf20Sopenharmony_ci						 csk->advmss);
19618c2ecf20Sopenharmony_ci			max_num_pdu = max_iso_data / max_pdu_size;
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci			num_pdu = (remaining_data_tosend +
19648c2ecf20Sopenharmony_ci				   tdata->max_xmit_dlength - 1) /
19658c2ecf20Sopenharmony_ci				  tdata->max_xmit_dlength;
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci			if (num_pdu > max_num_pdu)
19688c2ecf20Sopenharmony_ci				num_pdu = max_num_pdu;
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci			conn->max_xmit_dlength = tdata->max_xmit_dlength * num_pdu;
19718c2ecf20Sopenharmony_ci			max_txdata_len = conn->max_xmit_dlength;
19728c2ecf20Sopenharmony_ci			iso_tx_rsvd = cdev->skb_iso_txhdr;
19738c2ecf20Sopenharmony_ci			local_iso_info = sizeof(struct cxgbi_iso_info);
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci			log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
19768c2ecf20Sopenharmony_ci				  "max_pdu_size %u, max_num_pdu %u, "
19778c2ecf20Sopenharmony_ci				  "max_txdata %u, num_pdu %u\n",
19788c2ecf20Sopenharmony_ci				  max_pdu_size, max_num_pdu,
19798c2ecf20Sopenharmony_ci				  max_txdata_len, num_pdu);
19808c2ecf20Sopenharmony_ci		}
19818c2ecf20Sopenharmony_cino_iso:
19828c2ecf20Sopenharmony_ci		count  = min_t(u32, max_txdata_len, remaining_data_tosend);
19838c2ecf20Sopenharmony_ci		err = cxgbi_task_data_sgl_read(task,
19848c2ecf20Sopenharmony_ci					       tdata->offset + tdata->count,
19858c2ecf20Sopenharmony_ci					       count, &dlimit);
19868c2ecf20Sopenharmony_ci		if (unlikely(err < 0)) {
19878c2ecf20Sopenharmony_ci			log_debug(1 << CXGBI_DBG_ISCSI,
19888c2ecf20Sopenharmony_ci				  "task 0x%p, tcp_task 0x%p, tdata 0x%p, "
19898c2ecf20Sopenharmony_ci				  "sgl err %d, count %u, dlimit %u\n",
19908c2ecf20Sopenharmony_ci				  task, tcp_task, tdata, err, count, dlimit);
19918c2ecf20Sopenharmony_ci			if (dlimit) {
19928c2ecf20Sopenharmony_ci				remaining_data_tosend =
19938c2ecf20Sopenharmony_ci					rounddown(dlimit,
19948c2ecf20Sopenharmony_ci						  tdata->max_xmit_dlength);
19958c2ecf20Sopenharmony_ci				if (!remaining_data_tosend)
19968c2ecf20Sopenharmony_ci					remaining_data_tosend = dlimit;
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci				dlimit = 0;
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci				conn->max_xmit_dlength = remaining_data_tosend;
20018c2ecf20Sopenharmony_ci				goto recalculate_sgl;
20028c2ecf20Sopenharmony_ci			}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci			pr_err("task 0x%p, tcp_task 0x%p, tdata 0x%p, "
20058c2ecf20Sopenharmony_ci				"sgl err %d\n",
20068c2ecf20Sopenharmony_ci				task, tcp_task, tdata, err);
20078c2ecf20Sopenharmony_ci			goto ret_err;
20088c2ecf20Sopenharmony_ci		}
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci		if ((tdata->flags & CXGBI_TASK_SGL_COPY) ||
20118c2ecf20Sopenharmony_ci		    (tdata->nr_frags > MAX_SKB_FRAGS))
20128c2ecf20Sopenharmony_ci			headroom += conn->max_xmit_dlength;
20138c2ecf20Sopenharmony_ci	}
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	tdata->skb = alloc_skb(local_iso_info + cdev->skb_tx_rsvd +
20168c2ecf20Sopenharmony_ci			       iso_tx_rsvd + headroom, GFP_ATOMIC);
20178c2ecf20Sopenharmony_ci	if (!tdata->skb) {
20188c2ecf20Sopenharmony_ci		tdata->count = last_tdata_count;
20198c2ecf20Sopenharmony_ci		tdata->offset = last_tdata_offset;
20208c2ecf20Sopenharmony_ci		err = -ENOMEM;
20218c2ecf20Sopenharmony_ci		goto ret_err;
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	skb_reserve(tdata->skb, local_iso_info + cdev->skb_tx_rsvd +
20258c2ecf20Sopenharmony_ci		    iso_tx_rsvd);
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	if (task->sc) {
20288c2ecf20Sopenharmony_ci		task->hdr = (struct iscsi_hdr *)tdata->skb->data;
20298c2ecf20Sopenharmony_ci	} else {
20308c2ecf20Sopenharmony_ci		task->hdr = kzalloc(SKB_TX_ISCSI_PDU_HEADER_MAX, GFP_ATOMIC);
20318c2ecf20Sopenharmony_ci		if (!task->hdr) {
20328c2ecf20Sopenharmony_ci			__kfree_skb(tdata->skb);
20338c2ecf20Sopenharmony_ci			tdata->skb = NULL;
20348c2ecf20Sopenharmony_ci			return -ENOMEM;
20358c2ecf20Sopenharmony_ci		}
20368c2ecf20Sopenharmony_ci	}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	task->hdr_max = SKB_TX_ISCSI_PDU_HEADER_MAX;
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	if (iso_tx_rsvd)
20418c2ecf20Sopenharmony_ci		cxgbi_skcb_set_flag(tdata->skb, SKCBF_TX_ISO);
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	/* data_out uses scsi_cmd's itt */
20448c2ecf20Sopenharmony_ci	if (op != ISCSI_OP_SCSI_DATA_OUT)
20458c2ecf20Sopenharmony_ci		task_reserve_itt(task, &task->hdr->itt);
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
20488c2ecf20Sopenharmony_ci		  "task 0x%p, op 0x%x, skb 0x%p,%u+%u/%u, itt 0x%x.\n",
20498c2ecf20Sopenharmony_ci		  task, op, tdata->skb, cdev->skb_tx_rsvd, headroom,
20508c2ecf20Sopenharmony_ci		  conn->max_xmit_dlength, be32_to_cpu(task->hdr->itt));
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	return 0;
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ciret_err:
20558c2ecf20Sopenharmony_ci	conn->max_xmit_dlength = tdata->max_xmit_dlength;
20568c2ecf20Sopenharmony_ci	return err;
20578c2ecf20Sopenharmony_ci}
20588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_conn_alloc_pdu);
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_cistatic int
20618c2ecf20Sopenharmony_cicxgbi_prep_iso_info(struct iscsi_task *task, struct sk_buff *skb,
20628c2ecf20Sopenharmony_ci		    u32 count)
20638c2ecf20Sopenharmony_ci{
20648c2ecf20Sopenharmony_ci	struct cxgbi_iso_info *iso_info = (struct cxgbi_iso_info *)skb->head;
20658c2ecf20Sopenharmony_ci	struct iscsi_r2t_info *r2t;
20668c2ecf20Sopenharmony_ci	struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
20678c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = task->conn;
20688c2ecf20Sopenharmony_ci	struct iscsi_session *session = conn->session;
20698c2ecf20Sopenharmony_ci	struct iscsi_tcp_task *tcp_task = task->dd_data;
20708c2ecf20Sopenharmony_ci	u32 burst_size = 0, r2t_dlength = 0, dlength;
20718c2ecf20Sopenharmony_ci	u32 max_pdu_len = tdata->max_xmit_dlength;
20728c2ecf20Sopenharmony_ci	u32 segment_offset = 0;
20738c2ecf20Sopenharmony_ci	u32 num_pdu;
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	if (unlikely(!cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO)))
20768c2ecf20Sopenharmony_ci		return 0;
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	memset(iso_info, 0, sizeof(struct cxgbi_iso_info));
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	if (task->hdr->opcode == ISCSI_OP_SCSI_CMD && session->imm_data_en) {
20818c2ecf20Sopenharmony_ci		iso_info->flags |= CXGBI_ISO_INFO_IMM_ENABLE;
20828c2ecf20Sopenharmony_ci		burst_size = count;
20838c2ecf20Sopenharmony_ci	}
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci	dlength = ntoh24(task->hdr->dlength);
20868c2ecf20Sopenharmony_ci	dlength = min(dlength, max_pdu_len);
20878c2ecf20Sopenharmony_ci	hton24(task->hdr->dlength, dlength);
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci	num_pdu = (count + max_pdu_len - 1) / max_pdu_len;
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	if (iscsi_task_has_unsol_data(task))
20928c2ecf20Sopenharmony_ci		r2t = &task->unsol_r2t;
20938c2ecf20Sopenharmony_ci	else
20948c2ecf20Sopenharmony_ci		r2t = tcp_task->r2t;
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	if (r2t) {
20978c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
20988c2ecf20Sopenharmony_ci			  "count %u, tdata->count %u, num_pdu %u,"
20998c2ecf20Sopenharmony_ci			  "task->hdr_len %u, r2t->data_length %u, r2t->sent %u\n",
21008c2ecf20Sopenharmony_ci			  count, tdata->count, num_pdu, task->hdr_len,
21018c2ecf20Sopenharmony_ci			  r2t->data_length, r2t->sent);
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci		r2t_dlength = r2t->data_length - r2t->sent;
21048c2ecf20Sopenharmony_ci		segment_offset = r2t->sent;
21058c2ecf20Sopenharmony_ci		r2t->datasn += num_pdu - 1;
21068c2ecf20Sopenharmony_ci	}
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	if (!r2t || !r2t->sent)
21098c2ecf20Sopenharmony_ci		iso_info->flags |= CXGBI_ISO_INFO_FSLICE;
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	if (task->hdr->flags & ISCSI_FLAG_CMD_FINAL)
21128c2ecf20Sopenharmony_ci		iso_info->flags |= CXGBI_ISO_INFO_LSLICE;
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	task->hdr->flags &= ~ISCSI_FLAG_CMD_FINAL;
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci	iso_info->op = task->hdr->opcode;
21178c2ecf20Sopenharmony_ci	iso_info->ahs = task->hdr->hlength;
21188c2ecf20Sopenharmony_ci	iso_info->num_pdu = num_pdu;
21198c2ecf20Sopenharmony_ci	iso_info->mpdu = max_pdu_len;
21208c2ecf20Sopenharmony_ci	iso_info->burst_size = (burst_size + r2t_dlength) >> 2;
21218c2ecf20Sopenharmony_ci	iso_info->len = count + task->hdr_len;
21228c2ecf20Sopenharmony_ci	iso_info->segment_offset = segment_offset;
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	cxgbi_skcb_tx_iscsi_hdrlen(skb) = task->hdr_len;
21258c2ecf20Sopenharmony_ci	return 0;
21268c2ecf20Sopenharmony_ci}
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_cistatic inline void tx_skb_setmode(struct sk_buff *skb, int hcrc, int dcrc)
21298c2ecf20Sopenharmony_ci{
21308c2ecf20Sopenharmony_ci	if (hcrc || dcrc) {
21318c2ecf20Sopenharmony_ci		u8 submode = 0;
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci		if (hcrc)
21348c2ecf20Sopenharmony_ci			submode |= 1;
21358c2ecf20Sopenharmony_ci		if (dcrc)
21368c2ecf20Sopenharmony_ci			submode |= 2;
21378c2ecf20Sopenharmony_ci		cxgbi_skcb_tx_ulp_mode(skb) = (ULP2_MODE_ISCSI << 4) | submode;
21388c2ecf20Sopenharmony_ci	} else
21398c2ecf20Sopenharmony_ci		cxgbi_skcb_tx_ulp_mode(skb) = 0;
21408c2ecf20Sopenharmony_ci}
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_cistatic struct page *rsvd_page;
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ciint cxgbi_conn_init_pdu(struct iscsi_task *task, unsigned int offset,
21458c2ecf20Sopenharmony_ci			      unsigned int count)
21468c2ecf20Sopenharmony_ci{
21478c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = task->conn;
21488c2ecf20Sopenharmony_ci	struct iscsi_tcp_task *tcp_task = task->dd_data;
21498c2ecf20Sopenharmony_ci	struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
21508c2ecf20Sopenharmony_ci	struct sk_buff *skb;
21518c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc = task->sc;
21528c2ecf20Sopenharmony_ci	u32 expected_count, expected_offset;
21538c2ecf20Sopenharmony_ci	u32 datalen = count, dlimit = 0;
21548c2ecf20Sopenharmony_ci	u32 i, padlen = iscsi_padding(count);
21558c2ecf20Sopenharmony_ci	struct page *pg;
21568c2ecf20Sopenharmony_ci	int err;
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	if (!tcp_task || (tcp_task->dd_data != tdata)) {
21598c2ecf20Sopenharmony_ci		pr_err("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p.\n",
21608c2ecf20Sopenharmony_ci		       task, task->sc, tcp_task,
21618c2ecf20Sopenharmony_ci		       tcp_task ? tcp_task->dd_data : NULL, tdata);
21628c2ecf20Sopenharmony_ci		return -EINVAL;
21638c2ecf20Sopenharmony_ci	}
21648c2ecf20Sopenharmony_ci	skb = tdata->skb;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
21678c2ecf20Sopenharmony_ci		  "task 0x%p,0x%p, skb 0x%p, 0x%x,0x%x,0x%x, %u+%u.\n",
21688c2ecf20Sopenharmony_ci		  task, task->sc, skb, (*skb->data) & ISCSI_OPCODE_MASK,
21698c2ecf20Sopenharmony_ci		  be32_to_cpu(task->cmdsn), be32_to_cpu(task->hdr->itt), offset, count);
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci	skb_put(skb, task->hdr_len);
21728c2ecf20Sopenharmony_ci	tx_skb_setmode(skb, conn->hdrdgst_en, datalen ? conn->datadgst_en : 0);
21738c2ecf20Sopenharmony_ci	if (!count) {
21748c2ecf20Sopenharmony_ci		tdata->count = count;
21758c2ecf20Sopenharmony_ci		tdata->offset = offset;
21768c2ecf20Sopenharmony_ci		tdata->nr_frags = 0;
21778c2ecf20Sopenharmony_ci		tdata->total_offset = 0;
21788c2ecf20Sopenharmony_ci		tdata->total_count = 0;
21798c2ecf20Sopenharmony_ci		if (tdata->max_xmit_dlength)
21808c2ecf20Sopenharmony_ci			conn->max_xmit_dlength = tdata->max_xmit_dlength;
21818c2ecf20Sopenharmony_ci		cxgbi_skcb_clear_flag(skb, SKCBF_TX_ISO);
21828c2ecf20Sopenharmony_ci		return 0;
21838c2ecf20Sopenharmony_ci	}
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
21868c2ecf20Sopenharmony_ci		  "data->total_count %u, tdata->total_offset %u\n",
21878c2ecf20Sopenharmony_ci		  tdata->total_count, tdata->total_offset);
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	expected_count = tdata->total_count;
21908c2ecf20Sopenharmony_ci	expected_offset = tdata->total_offset;
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	if ((count != expected_count) ||
21938c2ecf20Sopenharmony_ci	    (offset != expected_offset)) {
21948c2ecf20Sopenharmony_ci		err = cxgbi_task_data_sgl_read(task, offset, count, &dlimit);
21958c2ecf20Sopenharmony_ci		if (err < 0) {
21968c2ecf20Sopenharmony_ci			pr_err("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p "
21978c2ecf20Sopenharmony_ci			       "dlimit %u, sgl err %d.\n", task, task->sc,
21988c2ecf20Sopenharmony_ci			       tcp_task, tcp_task ? tcp_task->dd_data : NULL,
21998c2ecf20Sopenharmony_ci			       tdata, dlimit, err);
22008c2ecf20Sopenharmony_ci			return err;
22018c2ecf20Sopenharmony_ci		}
22028c2ecf20Sopenharmony_ci	}
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	/* Restore original value of conn->max_xmit_dlength because
22058c2ecf20Sopenharmony_ci	 * it can get updated to ISO data size.
22068c2ecf20Sopenharmony_ci	 */
22078c2ecf20Sopenharmony_ci	conn->max_xmit_dlength = tdata->max_xmit_dlength;
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	if (sc) {
22108c2ecf20Sopenharmony_ci		struct page_frag *frag = tdata->frags;
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci		if ((tdata->flags & CXGBI_TASK_SGL_COPY) ||
22138c2ecf20Sopenharmony_ci		    (tdata->nr_frags > MAX_SKB_FRAGS) ||
22148c2ecf20Sopenharmony_ci		    (padlen && (tdata->nr_frags ==
22158c2ecf20Sopenharmony_ci					MAX_SKB_FRAGS))) {
22168c2ecf20Sopenharmony_ci			char *dst = skb->data + task->hdr_len;
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci			/* data fits in the skb's headroom */
22198c2ecf20Sopenharmony_ci			for (i = 0; i < tdata->nr_frags; i++, frag++) {
22208c2ecf20Sopenharmony_ci				char *src = kmap_atomic(frag->page);
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci				memcpy(dst, src + frag->offset, frag->size);
22238c2ecf20Sopenharmony_ci				dst += frag->size;
22248c2ecf20Sopenharmony_ci				kunmap_atomic(src);
22258c2ecf20Sopenharmony_ci			}
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci			if (padlen) {
22288c2ecf20Sopenharmony_ci				memset(dst, 0, padlen);
22298c2ecf20Sopenharmony_ci				padlen = 0;
22308c2ecf20Sopenharmony_ci			}
22318c2ecf20Sopenharmony_ci			skb_put(skb, count + padlen);
22328c2ecf20Sopenharmony_ci		} else {
22338c2ecf20Sopenharmony_ci			for (i = 0; i < tdata->nr_frags; i++, frag++) {
22348c2ecf20Sopenharmony_ci				get_page(frag->page);
22358c2ecf20Sopenharmony_ci				skb_fill_page_desc(skb, i, frag->page,
22368c2ecf20Sopenharmony_ci						   frag->offset, frag->size);
22378c2ecf20Sopenharmony_ci			}
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci			skb->len += count;
22408c2ecf20Sopenharmony_ci			skb->data_len += count;
22418c2ecf20Sopenharmony_ci			skb->truesize += count;
22428c2ecf20Sopenharmony_ci		}
22438c2ecf20Sopenharmony_ci	} else {
22448c2ecf20Sopenharmony_ci		pg = virt_to_head_page(task->data);
22458c2ecf20Sopenharmony_ci		get_page(pg);
22468c2ecf20Sopenharmony_ci		skb_fill_page_desc(skb, 0, pg,
22478c2ecf20Sopenharmony_ci				   task->data - (char *)page_address(pg),
22488c2ecf20Sopenharmony_ci				   count);
22498c2ecf20Sopenharmony_ci		skb->len += count;
22508c2ecf20Sopenharmony_ci		skb->data_len += count;
22518c2ecf20Sopenharmony_ci		skb->truesize += count;
22528c2ecf20Sopenharmony_ci	}
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	if (padlen) {
22558c2ecf20Sopenharmony_ci		get_page(rsvd_page);
22568c2ecf20Sopenharmony_ci		skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
22578c2ecf20Sopenharmony_ci				   rsvd_page, 0, padlen);
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci		skb->data_len += padlen;
22608c2ecf20Sopenharmony_ci		skb->truesize += padlen;
22618c2ecf20Sopenharmony_ci		skb->len += padlen;
22628c2ecf20Sopenharmony_ci	}
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ci	if (likely(count > tdata->max_xmit_dlength))
22658c2ecf20Sopenharmony_ci		cxgbi_prep_iso_info(task, skb, count);
22668c2ecf20Sopenharmony_ci	else
22678c2ecf20Sopenharmony_ci		cxgbi_skcb_clear_flag(skb, SKCBF_TX_ISO);
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	return 0;
22708c2ecf20Sopenharmony_ci}
22718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_conn_init_pdu);
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_cistatic int cxgbi_sock_tx_queue_up(struct cxgbi_sock *csk, struct sk_buff *skb)
22748c2ecf20Sopenharmony_ci{
22758c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = csk->cdev;
22768c2ecf20Sopenharmony_ci	struct cxgbi_iso_info *iso_cpl;
22778c2ecf20Sopenharmony_ci	u32 frags = skb_shinfo(skb)->nr_frags;
22788c2ecf20Sopenharmony_ci	u32 extra_len, num_pdu, hdr_len;
22798c2ecf20Sopenharmony_ci	u32 iso_tx_rsvd = 0;
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci	if (csk->state != CTP_ESTABLISHED) {
22828c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_TX,
22838c2ecf20Sopenharmony_ci			  "csk 0x%p,%u,0x%lx,%u, EAGAIN.\n",
22848c2ecf20Sopenharmony_ci			  csk, csk->state, csk->flags, csk->tid);
22858c2ecf20Sopenharmony_ci		return -EPIPE;
22868c2ecf20Sopenharmony_ci	}
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_ci	if (csk->err) {
22898c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_TX,
22908c2ecf20Sopenharmony_ci			  "csk 0x%p,%u,0x%lx,%u, EPIPE %d.\n",
22918c2ecf20Sopenharmony_ci			  csk, csk->state, csk->flags, csk->tid, csk->err);
22928c2ecf20Sopenharmony_ci		return -EPIPE;
22938c2ecf20Sopenharmony_ci	}
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	if ((cdev->flags & CXGBI_FLAG_DEV_T3) &&
22968c2ecf20Sopenharmony_ci	    before((csk->snd_win + csk->snd_una), csk->write_seq)) {
22978c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_TX,
22988c2ecf20Sopenharmony_ci			  "csk 0x%p,%u,0x%lx,%u, FULL %u-%u >= %u.\n",
22998c2ecf20Sopenharmony_ci			  csk, csk->state, csk->flags, csk->tid, csk->write_seq,
23008c2ecf20Sopenharmony_ci			  csk->snd_una, csk->snd_win);
23018c2ecf20Sopenharmony_ci		return -ENOBUFS;
23028c2ecf20Sopenharmony_ci	}
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	if (cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO))
23058c2ecf20Sopenharmony_ci		iso_tx_rsvd = cdev->skb_iso_txhdr;
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci	if (unlikely(skb_headroom(skb) < (cdev->skb_tx_rsvd + iso_tx_rsvd))) {
23088c2ecf20Sopenharmony_ci		pr_err("csk 0x%p, skb head %u < %u.\n",
23098c2ecf20Sopenharmony_ci		       csk, skb_headroom(skb), cdev->skb_tx_rsvd);
23108c2ecf20Sopenharmony_ci		return -EINVAL;
23118c2ecf20Sopenharmony_ci	}
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	if (skb->len != skb->data_len)
23148c2ecf20Sopenharmony_ci		frags++;
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci	if (frags >= SKB_WR_LIST_SIZE) {
23178c2ecf20Sopenharmony_ci		pr_err("csk 0x%p, frags %u, %u,%u >%lu.\n",
23188c2ecf20Sopenharmony_ci		       csk, skb_shinfo(skb)->nr_frags, skb->len,
23198c2ecf20Sopenharmony_ci		       skb->data_len, SKB_WR_LIST_SIZE);
23208c2ecf20Sopenharmony_ci		return -EINVAL;
23218c2ecf20Sopenharmony_ci	}
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	cxgbi_skcb_set_flag(skb, SKCBF_TX_NEED_HDR);
23248c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
23258c2ecf20Sopenharmony_ci	cxgbi_sock_skb_entail(csk, skb);
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci	extra_len = cxgbi_ulp_extra_len(cxgbi_skcb_tx_ulp_mode(skb));
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	if (likely(cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO))) {
23308c2ecf20Sopenharmony_ci		iso_cpl = (struct cxgbi_iso_info *)skb->head;
23318c2ecf20Sopenharmony_ci		num_pdu = iso_cpl->num_pdu;
23328c2ecf20Sopenharmony_ci		hdr_len = cxgbi_skcb_tx_iscsi_hdrlen(skb);
23338c2ecf20Sopenharmony_ci		extra_len = (cxgbi_ulp_extra_len(cxgbi_skcb_tx_ulp_mode(skb)) *
23348c2ecf20Sopenharmony_ci			     num_pdu) +	(hdr_len * (num_pdu - 1));
23358c2ecf20Sopenharmony_ci	}
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci	csk->write_seq += (skb->len + extra_len);
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci	return 0;
23408c2ecf20Sopenharmony_ci}
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_cistatic int cxgbi_sock_send_skb(struct cxgbi_sock *csk, struct sk_buff *skb)
23438c2ecf20Sopenharmony_ci{
23448c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = csk->cdev;
23458c2ecf20Sopenharmony_ci	int len = skb->len;
23468c2ecf20Sopenharmony_ci	int err;
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ci	spin_lock_bh(&csk->lock);
23498c2ecf20Sopenharmony_ci	err = cxgbi_sock_tx_queue_up(csk, skb);
23508c2ecf20Sopenharmony_ci	if (err < 0) {
23518c2ecf20Sopenharmony_ci		spin_unlock_bh(&csk->lock);
23528c2ecf20Sopenharmony_ci		return err;
23538c2ecf20Sopenharmony_ci	}
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_ci	if (likely(skb_queue_len(&csk->write_queue)))
23568c2ecf20Sopenharmony_ci		cdev->csk_push_tx_frames(csk, 0);
23578c2ecf20Sopenharmony_ci	spin_unlock_bh(&csk->lock);
23588c2ecf20Sopenharmony_ci	return len;
23598c2ecf20Sopenharmony_ci}
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ciint cxgbi_conn_xmit_pdu(struct iscsi_task *task)
23628c2ecf20Sopenharmony_ci{
23638c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
23648c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
23658c2ecf20Sopenharmony_ci	struct iscsi_tcp_task *tcp_task = task->dd_data;
23668c2ecf20Sopenharmony_ci	struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
23678c2ecf20Sopenharmony_ci	struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
23688c2ecf20Sopenharmony_ci	struct sk_buff *skb;
23698c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = NULL;
23708c2ecf20Sopenharmony_ci	u32 pdulen = 0;
23718c2ecf20Sopenharmony_ci	u32 datalen;
23728c2ecf20Sopenharmony_ci	int err;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	if (!tcp_task || (tcp_task->dd_data != tdata)) {
23758c2ecf20Sopenharmony_ci		pr_err("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p.\n",
23768c2ecf20Sopenharmony_ci		       task, task->sc, tcp_task,
23778c2ecf20Sopenharmony_ci		       tcp_task ? tcp_task->dd_data : NULL, tdata);
23788c2ecf20Sopenharmony_ci		return -EINVAL;
23798c2ecf20Sopenharmony_ci	}
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_ci	skb = tdata->skb;
23828c2ecf20Sopenharmony_ci	if (!skb) {
23838c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
23848c2ecf20Sopenharmony_ci			  "task 0x%p, skb NULL.\n", task);
23858c2ecf20Sopenharmony_ci		return 0;
23868c2ecf20Sopenharmony_ci	}
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	if (cconn && cconn->cep)
23898c2ecf20Sopenharmony_ci		csk = cconn->cep->csk;
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci	if (!csk) {
23928c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
23938c2ecf20Sopenharmony_ci			  "task 0x%p, csk gone.\n", task);
23948c2ecf20Sopenharmony_ci		return -EPIPE;
23958c2ecf20Sopenharmony_ci	}
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci	tdata->skb = NULL;
23988c2ecf20Sopenharmony_ci	datalen = skb->data_len;
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci	/* write ppod first if using ofldq to write ppod */
24018c2ecf20Sopenharmony_ci	if (ttinfo->flags & CXGBI_PPOD_INFO_FLAG_VALID) {
24028c2ecf20Sopenharmony_ci		struct cxgbi_ppm *ppm = csk->cdev->cdev2ppm(csk->cdev);
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci		ttinfo->flags &= ~CXGBI_PPOD_INFO_FLAG_VALID;
24058c2ecf20Sopenharmony_ci		if (csk->cdev->csk_ddp_set_map(ppm, csk, ttinfo) < 0)
24068c2ecf20Sopenharmony_ci			pr_err("task 0x%p, ppod writing using ofldq failed.\n",
24078c2ecf20Sopenharmony_ci			       task);
24088c2ecf20Sopenharmony_ci			/* continue. Let fl get the data */
24098c2ecf20Sopenharmony_ci	}
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	if (!task->sc)
24128c2ecf20Sopenharmony_ci		memcpy(skb->data, task->hdr, SKB_TX_ISCSI_PDU_HEADER_MAX);
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci	err = cxgbi_sock_send_skb(csk, skb);
24158c2ecf20Sopenharmony_ci	if (err > 0) {
24168c2ecf20Sopenharmony_ci		pdulen += err;
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_TX, "task 0x%p,0x%p, rv %d.\n",
24198c2ecf20Sopenharmony_ci			  task, task->sc, err);
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci		if (task->conn->hdrdgst_en)
24228c2ecf20Sopenharmony_ci			pdulen += ISCSI_DIGEST_SIZE;
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci		if (datalen && task->conn->datadgst_en)
24258c2ecf20Sopenharmony_ci			pdulen += ISCSI_DIGEST_SIZE;
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_ci		task->conn->txdata_octets += pdulen;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci		if (unlikely(cxgbi_is_iso_config(csk) && cxgbi_is_iso_disabled(csk))) {
24308c2ecf20Sopenharmony_ci			if (time_after(jiffies, csk->prev_iso_ts + HZ)) {
24318c2ecf20Sopenharmony_ci				csk->disable_iso = false;
24328c2ecf20Sopenharmony_ci				csk->prev_iso_ts = 0;
24338c2ecf20Sopenharmony_ci				log_debug(1 << CXGBI_DBG_PDU_TX,
24348c2ecf20Sopenharmony_ci					  "enable iso: csk 0x%p\n", csk);
24358c2ecf20Sopenharmony_ci			}
24368c2ecf20Sopenharmony_ci		}
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci		return 0;
24398c2ecf20Sopenharmony_ci	}
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci	if (err == -EAGAIN || err == -ENOBUFS) {
24428c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_PDU_TX,
24438c2ecf20Sopenharmony_ci			  "task 0x%p, skb 0x%p, len %u/%u, %d EAGAIN.\n",
24448c2ecf20Sopenharmony_ci			  task, skb, skb->len, skb->data_len, err);
24458c2ecf20Sopenharmony_ci		/* reset skb to send when we are called again */
24468c2ecf20Sopenharmony_ci		tdata->skb = skb;
24478c2ecf20Sopenharmony_ci
24488c2ecf20Sopenharmony_ci		if (cxgbi_is_iso_config(csk) && !cxgbi_is_iso_disabled(csk) &&
24498c2ecf20Sopenharmony_ci		    (csk->no_tx_credits++ >= 2)) {
24508c2ecf20Sopenharmony_ci			csk->disable_iso = true;
24518c2ecf20Sopenharmony_ci			csk->prev_iso_ts = jiffies;
24528c2ecf20Sopenharmony_ci			log_debug(1 << CXGBI_DBG_PDU_TX,
24538c2ecf20Sopenharmony_ci				  "disable iso:csk 0x%p, ts:%lu\n",
24548c2ecf20Sopenharmony_ci				  csk, csk->prev_iso_ts);
24558c2ecf20Sopenharmony_ci		}
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci		return err;
24588c2ecf20Sopenharmony_ci	}
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
24618c2ecf20Sopenharmony_ci		  "itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
24628c2ecf20Sopenharmony_ci		  task->itt, skb, skb->len, skb->data_len, err);
24638c2ecf20Sopenharmony_ci	__kfree_skb(skb);
24648c2ecf20Sopenharmony_ci	iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
24658c2ecf20Sopenharmony_ci	iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
24668c2ecf20Sopenharmony_ci	return err;
24678c2ecf20Sopenharmony_ci}
24688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_conn_xmit_pdu);
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_civoid cxgbi_cleanup_task(struct iscsi_task *task)
24718c2ecf20Sopenharmony_ci{
24728c2ecf20Sopenharmony_ci	struct iscsi_tcp_task *tcp_task = task->dd_data;
24738c2ecf20Sopenharmony_ci	struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci	if (!tcp_task || (tcp_task->dd_data != tdata)) {
24768c2ecf20Sopenharmony_ci		pr_info("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p.\n",
24778c2ecf20Sopenharmony_ci			task, task->sc, tcp_task,
24788c2ecf20Sopenharmony_ci			tcp_task ? tcp_task->dd_data : NULL, tdata);
24798c2ecf20Sopenharmony_ci		return;
24808c2ecf20Sopenharmony_ci	}
24818c2ecf20Sopenharmony_ci
24828c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
24838c2ecf20Sopenharmony_ci		"task 0x%p, skb 0x%p, itt 0x%x.\n",
24848c2ecf20Sopenharmony_ci		task, tdata->skb, task->hdr_itt);
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci	tcp_task->dd_data = NULL;
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	if (!task->sc)
24898c2ecf20Sopenharmony_ci		kfree(task->hdr);
24908c2ecf20Sopenharmony_ci	task->hdr = NULL;
24918c2ecf20Sopenharmony_ci
24928c2ecf20Sopenharmony_ci	/*  never reached the xmit task callout */
24938c2ecf20Sopenharmony_ci	if (tdata->skb) {
24948c2ecf20Sopenharmony_ci		__kfree_skb(tdata->skb);
24958c2ecf20Sopenharmony_ci		tdata->skb = NULL;
24968c2ecf20Sopenharmony_ci	}
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci	task_release_itt(task, task->hdr_itt);
24998c2ecf20Sopenharmony_ci	memset(tdata, 0, sizeof(*tdata));
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_ci	iscsi_tcp_cleanup_task(task);
25028c2ecf20Sopenharmony_ci}
25038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_cleanup_task);
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_civoid cxgbi_get_conn_stats(struct iscsi_cls_conn *cls_conn,
25068c2ecf20Sopenharmony_ci				struct iscsi_stats *stats)
25078c2ecf20Sopenharmony_ci{
25088c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
25098c2ecf20Sopenharmony_ci
25108c2ecf20Sopenharmony_ci	stats->txdata_octets = conn->txdata_octets;
25118c2ecf20Sopenharmony_ci	stats->rxdata_octets = conn->rxdata_octets;
25128c2ecf20Sopenharmony_ci	stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
25138c2ecf20Sopenharmony_ci	stats->dataout_pdus = conn->dataout_pdus_cnt;
25148c2ecf20Sopenharmony_ci	stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
25158c2ecf20Sopenharmony_ci	stats->datain_pdus = conn->datain_pdus_cnt;
25168c2ecf20Sopenharmony_ci	stats->r2t_pdus = conn->r2t_pdus_cnt;
25178c2ecf20Sopenharmony_ci	stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
25188c2ecf20Sopenharmony_ci	stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
25198c2ecf20Sopenharmony_ci	stats->digest_err = 0;
25208c2ecf20Sopenharmony_ci	stats->timeout_err = 0;
25218c2ecf20Sopenharmony_ci	stats->custom_length = 1;
25228c2ecf20Sopenharmony_ci	strcpy(stats->custom[0].desc, "eh_abort_cnt");
25238c2ecf20Sopenharmony_ci	stats->custom[0].value = conn->eh_abort_cnt;
25248c2ecf20Sopenharmony_ci}
25258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_get_conn_stats);
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_cistatic int cxgbi_conn_max_xmit_dlength(struct iscsi_conn *conn)
25288c2ecf20Sopenharmony_ci{
25298c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
25308c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
25318c2ecf20Sopenharmony_ci	struct cxgbi_device *cdev = cconn->chba->cdev;
25328c2ecf20Sopenharmony_ci	unsigned int headroom = SKB_MAX_HEAD(cdev->skb_tx_rsvd);
25338c2ecf20Sopenharmony_ci	unsigned int max_def = 512 * MAX_SKB_FRAGS;
25348c2ecf20Sopenharmony_ci	unsigned int max = max(max_def, headroom);
25358c2ecf20Sopenharmony_ci
25368c2ecf20Sopenharmony_ci	max = min(cconn->chba->cdev->tx_max_size, max);
25378c2ecf20Sopenharmony_ci	if (conn->max_xmit_dlength)
25388c2ecf20Sopenharmony_ci		conn->max_xmit_dlength = min(conn->max_xmit_dlength, max);
25398c2ecf20Sopenharmony_ci	else
25408c2ecf20Sopenharmony_ci		conn->max_xmit_dlength = max;
25418c2ecf20Sopenharmony_ci	cxgbi_align_pdu_size(conn->max_xmit_dlength);
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	return 0;
25448c2ecf20Sopenharmony_ci}
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_cistatic int cxgbi_conn_max_recv_dlength(struct iscsi_conn *conn)
25478c2ecf20Sopenharmony_ci{
25488c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
25498c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
25508c2ecf20Sopenharmony_ci	unsigned int max = cconn->chba->cdev->rx_max_size;
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci	cxgbi_align_pdu_size(max);
25538c2ecf20Sopenharmony_ci
25548c2ecf20Sopenharmony_ci	if (conn->max_recv_dlength) {
25558c2ecf20Sopenharmony_ci		if (conn->max_recv_dlength > max) {
25568c2ecf20Sopenharmony_ci			pr_err("MaxRecvDataSegmentLength %u > %u.\n",
25578c2ecf20Sopenharmony_ci				conn->max_recv_dlength, max);
25588c2ecf20Sopenharmony_ci			return -EINVAL;
25598c2ecf20Sopenharmony_ci		}
25608c2ecf20Sopenharmony_ci		conn->max_recv_dlength = min(conn->max_recv_dlength, max);
25618c2ecf20Sopenharmony_ci		cxgbi_align_pdu_size(conn->max_recv_dlength);
25628c2ecf20Sopenharmony_ci	} else
25638c2ecf20Sopenharmony_ci		conn->max_recv_dlength = max;
25648c2ecf20Sopenharmony_ci
25658c2ecf20Sopenharmony_ci	return 0;
25668c2ecf20Sopenharmony_ci}
25678c2ecf20Sopenharmony_ci
25688c2ecf20Sopenharmony_ciint cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn,
25698c2ecf20Sopenharmony_ci			enum iscsi_param param, char *buf, int buflen)
25708c2ecf20Sopenharmony_ci{
25718c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
25728c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
25738c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
25748c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = cconn->cep->csk;
25758c2ecf20Sopenharmony_ci	int err;
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
25788c2ecf20Sopenharmony_ci		"cls_conn 0x%p, param %d, buf(%d) %s.\n",
25798c2ecf20Sopenharmony_ci		cls_conn, param, buflen, buf);
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	switch (param) {
25828c2ecf20Sopenharmony_ci	case ISCSI_PARAM_HDRDGST_EN:
25838c2ecf20Sopenharmony_ci		err = iscsi_set_param(cls_conn, param, buf, buflen);
25848c2ecf20Sopenharmony_ci		if (!err && conn->hdrdgst_en)
25858c2ecf20Sopenharmony_ci			err = csk->cdev->csk_ddp_setup_digest(csk, csk->tid,
25868c2ecf20Sopenharmony_ci							conn->hdrdgst_en,
25878c2ecf20Sopenharmony_ci							conn->datadgst_en);
25888c2ecf20Sopenharmony_ci		break;
25898c2ecf20Sopenharmony_ci	case ISCSI_PARAM_DATADGST_EN:
25908c2ecf20Sopenharmony_ci		err = iscsi_set_param(cls_conn, param, buf, buflen);
25918c2ecf20Sopenharmony_ci		if (!err && conn->datadgst_en)
25928c2ecf20Sopenharmony_ci			err = csk->cdev->csk_ddp_setup_digest(csk, csk->tid,
25938c2ecf20Sopenharmony_ci							conn->hdrdgst_en,
25948c2ecf20Sopenharmony_ci							conn->datadgst_en);
25958c2ecf20Sopenharmony_ci		break;
25968c2ecf20Sopenharmony_ci	case ISCSI_PARAM_MAX_R2T:
25978c2ecf20Sopenharmony_ci		return iscsi_tcp_set_max_r2t(conn, buf);
25988c2ecf20Sopenharmony_ci	case ISCSI_PARAM_MAX_RECV_DLENGTH:
25998c2ecf20Sopenharmony_ci		err = iscsi_set_param(cls_conn, param, buf, buflen);
26008c2ecf20Sopenharmony_ci		if (!err)
26018c2ecf20Sopenharmony_ci			err = cxgbi_conn_max_recv_dlength(conn);
26028c2ecf20Sopenharmony_ci		break;
26038c2ecf20Sopenharmony_ci	case ISCSI_PARAM_MAX_XMIT_DLENGTH:
26048c2ecf20Sopenharmony_ci		err = iscsi_set_param(cls_conn, param, buf, buflen);
26058c2ecf20Sopenharmony_ci		if (!err)
26068c2ecf20Sopenharmony_ci			err = cxgbi_conn_max_xmit_dlength(conn);
26078c2ecf20Sopenharmony_ci		break;
26088c2ecf20Sopenharmony_ci	default:
26098c2ecf20Sopenharmony_ci		return iscsi_set_param(cls_conn, param, buf, buflen);
26108c2ecf20Sopenharmony_ci	}
26118c2ecf20Sopenharmony_ci	return err;
26128c2ecf20Sopenharmony_ci}
26138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_set_conn_param);
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ciint cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param,
26168c2ecf20Sopenharmony_ci		       char *buf)
26178c2ecf20Sopenharmony_ci{
26188c2ecf20Sopenharmony_ci	struct cxgbi_endpoint *cep = ep->dd_data;
26198c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk;
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
26228c2ecf20Sopenharmony_ci		"cls_conn 0x%p, param %d.\n", ep, param);
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_ci	switch (param) {
26258c2ecf20Sopenharmony_ci	case ISCSI_PARAM_CONN_PORT:
26268c2ecf20Sopenharmony_ci	case ISCSI_PARAM_CONN_ADDRESS:
26278c2ecf20Sopenharmony_ci		if (!cep)
26288c2ecf20Sopenharmony_ci			return -ENOTCONN;
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_ci		csk = cep->csk;
26318c2ecf20Sopenharmony_ci		if (!csk)
26328c2ecf20Sopenharmony_ci			return -ENOTCONN;
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ci		return iscsi_conn_get_addr_param((struct sockaddr_storage *)
26358c2ecf20Sopenharmony_ci						 &csk->daddr, param, buf);
26368c2ecf20Sopenharmony_ci	default:
26378c2ecf20Sopenharmony_ci		break;
26388c2ecf20Sopenharmony_ci	}
26398c2ecf20Sopenharmony_ci	return -ENOSYS;
26408c2ecf20Sopenharmony_ci}
26418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_get_ep_param);
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_cistruct iscsi_cls_conn *
26448c2ecf20Sopenharmony_cicxgbi_create_conn(struct iscsi_cls_session *cls_session, u32 cid)
26458c2ecf20Sopenharmony_ci{
26468c2ecf20Sopenharmony_ci	struct iscsi_cls_conn *cls_conn;
26478c2ecf20Sopenharmony_ci	struct iscsi_conn *conn;
26488c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn;
26498c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn;
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci	cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*cconn), cid);
26528c2ecf20Sopenharmony_ci	if (!cls_conn)
26538c2ecf20Sopenharmony_ci		return NULL;
26548c2ecf20Sopenharmony_ci
26558c2ecf20Sopenharmony_ci	conn = cls_conn->dd_data;
26568c2ecf20Sopenharmony_ci	tcp_conn = conn->dd_data;
26578c2ecf20Sopenharmony_ci	cconn = tcp_conn->dd_data;
26588c2ecf20Sopenharmony_ci	cconn->iconn = conn;
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
26618c2ecf20Sopenharmony_ci		"cid %u(0x%x), cls 0x%p,0x%p, conn 0x%p,0x%p,0x%p.\n",
26628c2ecf20Sopenharmony_ci		cid, cid, cls_session, cls_conn, conn, tcp_conn, cconn);
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci	return cls_conn;
26658c2ecf20Sopenharmony_ci}
26668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_create_conn);
26678c2ecf20Sopenharmony_ci
26688c2ecf20Sopenharmony_ciint cxgbi_bind_conn(struct iscsi_cls_session *cls_session,
26698c2ecf20Sopenharmony_ci				struct iscsi_cls_conn *cls_conn,
26708c2ecf20Sopenharmony_ci				u64 transport_eph, int is_leading)
26718c2ecf20Sopenharmony_ci{
26728c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
26738c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
26748c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = tcp_conn->dd_data;
26758c2ecf20Sopenharmony_ci	struct cxgbi_ppm *ppm;
26768c2ecf20Sopenharmony_ci	struct iscsi_endpoint *ep;
26778c2ecf20Sopenharmony_ci	struct cxgbi_endpoint *cep;
26788c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk;
26798c2ecf20Sopenharmony_ci	int err;
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	ep = iscsi_lookup_endpoint(transport_eph);
26828c2ecf20Sopenharmony_ci	if (!ep)
26838c2ecf20Sopenharmony_ci		return -EINVAL;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	/*  setup ddp pagesize */
26868c2ecf20Sopenharmony_ci	cep = ep->dd_data;
26878c2ecf20Sopenharmony_ci	csk = cep->csk;
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_ci	ppm = csk->cdev->cdev2ppm(csk->cdev);
26908c2ecf20Sopenharmony_ci	err = csk->cdev->csk_ddp_setup_pgidx(csk, csk->tid,
26918c2ecf20Sopenharmony_ci					     ppm->tformat.pgsz_idx_dflt);
26928c2ecf20Sopenharmony_ci	if (err < 0)
26938c2ecf20Sopenharmony_ci		goto put_ep;
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_ci	err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
26968c2ecf20Sopenharmony_ci	if (err) {
26978c2ecf20Sopenharmony_ci		err = -EINVAL;
26988c2ecf20Sopenharmony_ci		goto put_ep;
26998c2ecf20Sopenharmony_ci	}
27008c2ecf20Sopenharmony_ci
27018c2ecf20Sopenharmony_ci	/*  calculate the tag idx bits needed for this conn based on cmds_max */
27028c2ecf20Sopenharmony_ci	cconn->task_idx_bits = (__ilog2_u32(conn->session->cmds_max - 1)) + 1;
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	write_lock_bh(&csk->callback_lock);
27058c2ecf20Sopenharmony_ci	csk->user_data = conn;
27068c2ecf20Sopenharmony_ci	cconn->chba = cep->chba;
27078c2ecf20Sopenharmony_ci	cconn->cep = cep;
27088c2ecf20Sopenharmony_ci	cep->cconn = cconn;
27098c2ecf20Sopenharmony_ci	write_unlock_bh(&csk->callback_lock);
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ci	cxgbi_conn_max_xmit_dlength(conn);
27128c2ecf20Sopenharmony_ci	cxgbi_conn_max_recv_dlength(conn);
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
27158c2ecf20Sopenharmony_ci		"cls 0x%p,0x%p, ep 0x%p, cconn 0x%p, csk 0x%p.\n",
27168c2ecf20Sopenharmony_ci		cls_session, cls_conn, ep, cconn, csk);
27178c2ecf20Sopenharmony_ci	/*  init recv engine */
27188c2ecf20Sopenharmony_ci	iscsi_tcp_hdr_recv_prep(tcp_conn);
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ciput_ep:
27218c2ecf20Sopenharmony_ci	iscsi_put_endpoint(ep);
27228c2ecf20Sopenharmony_ci	return err;
27238c2ecf20Sopenharmony_ci}
27248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_bind_conn);
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_cistruct iscsi_cls_session *cxgbi_create_session(struct iscsi_endpoint *ep,
27278c2ecf20Sopenharmony_ci						u16 cmds_max, u16 qdepth,
27288c2ecf20Sopenharmony_ci						u32 initial_cmdsn)
27298c2ecf20Sopenharmony_ci{
27308c2ecf20Sopenharmony_ci	struct cxgbi_endpoint *cep;
27318c2ecf20Sopenharmony_ci	struct cxgbi_hba *chba;
27328c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
27338c2ecf20Sopenharmony_ci	struct iscsi_cls_session *cls_session;
27348c2ecf20Sopenharmony_ci	struct iscsi_session *session;
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci	if (!ep) {
27378c2ecf20Sopenharmony_ci		pr_err("missing endpoint.\n");
27388c2ecf20Sopenharmony_ci		return NULL;
27398c2ecf20Sopenharmony_ci	}
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_ci	cep = ep->dd_data;
27428c2ecf20Sopenharmony_ci	chba = cep->chba;
27438c2ecf20Sopenharmony_ci	shost = chba->shost;
27448c2ecf20Sopenharmony_ci
27458c2ecf20Sopenharmony_ci	BUG_ON(chba != iscsi_host_priv(shost));
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	cls_session = iscsi_session_setup(chba->cdev->itp, shost,
27488c2ecf20Sopenharmony_ci					cmds_max, 0,
27498c2ecf20Sopenharmony_ci					sizeof(struct iscsi_tcp_task) +
27508c2ecf20Sopenharmony_ci					sizeof(struct cxgbi_task_data),
27518c2ecf20Sopenharmony_ci					initial_cmdsn, ISCSI_MAX_TARGET);
27528c2ecf20Sopenharmony_ci	if (!cls_session)
27538c2ecf20Sopenharmony_ci		return NULL;
27548c2ecf20Sopenharmony_ci
27558c2ecf20Sopenharmony_ci	session = cls_session->dd_data;
27568c2ecf20Sopenharmony_ci	if (iscsi_tcp_r2tpool_alloc(session))
27578c2ecf20Sopenharmony_ci		goto remove_session;
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
27608c2ecf20Sopenharmony_ci		"ep 0x%p, cls sess 0x%p.\n", ep, cls_session);
27618c2ecf20Sopenharmony_ci	return cls_session;
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ciremove_session:
27648c2ecf20Sopenharmony_ci	iscsi_session_teardown(cls_session);
27658c2ecf20Sopenharmony_ci	return NULL;
27668c2ecf20Sopenharmony_ci}
27678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_create_session);
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_civoid cxgbi_destroy_session(struct iscsi_cls_session *cls_session)
27708c2ecf20Sopenharmony_ci{
27718c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
27728c2ecf20Sopenharmony_ci		"cls sess 0x%p.\n", cls_session);
27738c2ecf20Sopenharmony_ci
27748c2ecf20Sopenharmony_ci	iscsi_tcp_r2tpool_free(cls_session->dd_data);
27758c2ecf20Sopenharmony_ci	iscsi_session_teardown(cls_session);
27768c2ecf20Sopenharmony_ci}
27778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_destroy_session);
27788c2ecf20Sopenharmony_ci
27798c2ecf20Sopenharmony_ciint cxgbi_set_host_param(struct Scsi_Host *shost, enum iscsi_host_param param,
27808c2ecf20Sopenharmony_ci			char *buf, int buflen)
27818c2ecf20Sopenharmony_ci{
27828c2ecf20Sopenharmony_ci	struct cxgbi_hba *chba = iscsi_host_priv(shost);
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci	if (!chba->ndev) {
27858c2ecf20Sopenharmony_ci		shost_printk(KERN_ERR, shost, "Could not get host param. "
27868c2ecf20Sopenharmony_ci				"netdev for host not set.\n");
27878c2ecf20Sopenharmony_ci		return -ENODEV;
27888c2ecf20Sopenharmony_ci	}
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
27918c2ecf20Sopenharmony_ci		"shost 0x%p, hba 0x%p,%s, param %d, buf(%d) %s.\n",
27928c2ecf20Sopenharmony_ci		shost, chba, chba->ndev->name, param, buflen, buf);
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	switch (param) {
27958c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM_IPADDRESS:
27968c2ecf20Sopenharmony_ci	{
27978c2ecf20Sopenharmony_ci		__be32 addr = in_aton(buf);
27988c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_ISCSI,
27998c2ecf20Sopenharmony_ci			"hba %s, req. ipv4 %pI4.\n", chba->ndev->name, &addr);
28008c2ecf20Sopenharmony_ci		cxgbi_set_iscsi_ipv4(chba, addr);
28018c2ecf20Sopenharmony_ci		return 0;
28028c2ecf20Sopenharmony_ci	}
28038c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM_HWADDRESS:
28048c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM_NETDEV_NAME:
28058c2ecf20Sopenharmony_ci		return 0;
28068c2ecf20Sopenharmony_ci	default:
28078c2ecf20Sopenharmony_ci		return iscsi_host_set_param(shost, param, buf, buflen);
28088c2ecf20Sopenharmony_ci	}
28098c2ecf20Sopenharmony_ci}
28108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_set_host_param);
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ciint cxgbi_get_host_param(struct Scsi_Host *shost, enum iscsi_host_param param,
28138c2ecf20Sopenharmony_ci			char *buf)
28148c2ecf20Sopenharmony_ci{
28158c2ecf20Sopenharmony_ci	struct cxgbi_hba *chba = iscsi_host_priv(shost);
28168c2ecf20Sopenharmony_ci	int len = 0;
28178c2ecf20Sopenharmony_ci
28188c2ecf20Sopenharmony_ci	if (!chba->ndev) {
28198c2ecf20Sopenharmony_ci		shost_printk(KERN_ERR, shost, "Could not get host param. "
28208c2ecf20Sopenharmony_ci				"netdev for host not set.\n");
28218c2ecf20Sopenharmony_ci		return -ENODEV;
28228c2ecf20Sopenharmony_ci	}
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
28258c2ecf20Sopenharmony_ci		"shost 0x%p, hba 0x%p,%s, param %d.\n",
28268c2ecf20Sopenharmony_ci		shost, chba, chba->ndev->name, param);
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_ci	switch (param) {
28298c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM_HWADDRESS:
28308c2ecf20Sopenharmony_ci		len = sysfs_format_mac(buf, chba->ndev->dev_addr, 6);
28318c2ecf20Sopenharmony_ci		break;
28328c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM_NETDEV_NAME:
28338c2ecf20Sopenharmony_ci		len = sprintf(buf, "%s\n", chba->ndev->name);
28348c2ecf20Sopenharmony_ci		break;
28358c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM_IPADDRESS:
28368c2ecf20Sopenharmony_ci	{
28378c2ecf20Sopenharmony_ci		struct cxgbi_sock *csk = find_sock_on_port(chba->cdev,
28388c2ecf20Sopenharmony_ci							   chba->port_id);
28398c2ecf20Sopenharmony_ci		if (csk) {
28408c2ecf20Sopenharmony_ci			len = sprintf(buf, "%pIS",
28418c2ecf20Sopenharmony_ci				      (struct sockaddr *)&csk->saddr);
28428c2ecf20Sopenharmony_ci		}
28438c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_ISCSI,
28448c2ecf20Sopenharmony_ci			  "hba %s, addr %s.\n", chba->ndev->name, buf);
28458c2ecf20Sopenharmony_ci		break;
28468c2ecf20Sopenharmony_ci	}
28478c2ecf20Sopenharmony_ci	default:
28488c2ecf20Sopenharmony_ci		return iscsi_host_get_param(shost, param, buf);
28498c2ecf20Sopenharmony_ci	}
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_ci	return len;
28528c2ecf20Sopenharmony_ci}
28538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_get_host_param);
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_cistruct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost,
28568c2ecf20Sopenharmony_ci					struct sockaddr *dst_addr,
28578c2ecf20Sopenharmony_ci					int non_blocking)
28588c2ecf20Sopenharmony_ci{
28598c2ecf20Sopenharmony_ci	struct iscsi_endpoint *ep;
28608c2ecf20Sopenharmony_ci	struct cxgbi_endpoint *cep;
28618c2ecf20Sopenharmony_ci	struct cxgbi_hba *hba = NULL;
28628c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk;
28638c2ecf20Sopenharmony_ci	int ifindex = 0;
28648c2ecf20Sopenharmony_ci	int err = -EINVAL;
28658c2ecf20Sopenharmony_ci
28668c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_SOCK,
28678c2ecf20Sopenharmony_ci		"shost 0x%p, non_blocking %d, dst_addr 0x%p.\n",
28688c2ecf20Sopenharmony_ci		shost, non_blocking, dst_addr);
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_ci	if (shost) {
28718c2ecf20Sopenharmony_ci		hba = iscsi_host_priv(shost);
28728c2ecf20Sopenharmony_ci		if (!hba) {
28738c2ecf20Sopenharmony_ci			pr_info("shost 0x%p, priv NULL.\n", shost);
28748c2ecf20Sopenharmony_ci			goto err_out;
28758c2ecf20Sopenharmony_ci		}
28768c2ecf20Sopenharmony_ci	}
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_cicheck_route:
28798c2ecf20Sopenharmony_ci	if (dst_addr->sa_family == AF_INET) {
28808c2ecf20Sopenharmony_ci		csk = cxgbi_check_route(dst_addr, ifindex);
28818c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
28828c2ecf20Sopenharmony_ci	} else if (dst_addr->sa_family == AF_INET6) {
28838c2ecf20Sopenharmony_ci		csk = cxgbi_check_route6(dst_addr, ifindex);
28848c2ecf20Sopenharmony_ci#endif
28858c2ecf20Sopenharmony_ci	} else {
28868c2ecf20Sopenharmony_ci		pr_info("address family 0x%x NOT supported.\n",
28878c2ecf20Sopenharmony_ci			dst_addr->sa_family);
28888c2ecf20Sopenharmony_ci		err = -EAFNOSUPPORT;
28898c2ecf20Sopenharmony_ci		return (struct iscsi_endpoint *)ERR_PTR(err);
28908c2ecf20Sopenharmony_ci	}
28918c2ecf20Sopenharmony_ci
28928c2ecf20Sopenharmony_ci	if (IS_ERR(csk))
28938c2ecf20Sopenharmony_ci		return (struct iscsi_endpoint *)csk;
28948c2ecf20Sopenharmony_ci	cxgbi_sock_get(csk);
28958c2ecf20Sopenharmony_ci
28968c2ecf20Sopenharmony_ci	if (!hba)
28978c2ecf20Sopenharmony_ci		hba = csk->cdev->hbas[csk->port_id];
28988c2ecf20Sopenharmony_ci	else if (hba != csk->cdev->hbas[csk->port_id]) {
28998c2ecf20Sopenharmony_ci		if (ifindex != hba->ndev->ifindex) {
29008c2ecf20Sopenharmony_ci			cxgbi_sock_put(csk);
29018c2ecf20Sopenharmony_ci			cxgbi_sock_closed(csk);
29028c2ecf20Sopenharmony_ci			ifindex = hba->ndev->ifindex;
29038c2ecf20Sopenharmony_ci			goto check_route;
29048c2ecf20Sopenharmony_ci		}
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_ci		pr_info("Could not connect through requested host %u"
29078c2ecf20Sopenharmony_ci			"hba 0x%p != 0x%p (%u).\n",
29088c2ecf20Sopenharmony_ci			shost->host_no, hba,
29098c2ecf20Sopenharmony_ci			csk->cdev->hbas[csk->port_id], csk->port_id);
29108c2ecf20Sopenharmony_ci		err = -ENOSPC;
29118c2ecf20Sopenharmony_ci		goto release_conn;
29128c2ecf20Sopenharmony_ci	}
29138c2ecf20Sopenharmony_ci
29148c2ecf20Sopenharmony_ci	err = sock_get_port(csk);
29158c2ecf20Sopenharmony_ci	if (err)
29168c2ecf20Sopenharmony_ci		goto release_conn;
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci	cxgbi_sock_set_state(csk, CTP_CONNECTING);
29198c2ecf20Sopenharmony_ci	err = csk->cdev->csk_init_act_open(csk);
29208c2ecf20Sopenharmony_ci	if (err)
29218c2ecf20Sopenharmony_ci		goto release_conn;
29228c2ecf20Sopenharmony_ci
29238c2ecf20Sopenharmony_ci	if (cxgbi_sock_is_closing(csk)) {
29248c2ecf20Sopenharmony_ci		err = -ENOSPC;
29258c2ecf20Sopenharmony_ci		pr_info("csk 0x%p is closing.\n", csk);
29268c2ecf20Sopenharmony_ci		goto release_conn;
29278c2ecf20Sopenharmony_ci	}
29288c2ecf20Sopenharmony_ci
29298c2ecf20Sopenharmony_ci	ep = iscsi_create_endpoint(sizeof(*cep));
29308c2ecf20Sopenharmony_ci	if (!ep) {
29318c2ecf20Sopenharmony_ci		err = -ENOMEM;
29328c2ecf20Sopenharmony_ci		pr_info("iscsi alloc ep, OOM.\n");
29338c2ecf20Sopenharmony_ci		goto release_conn;
29348c2ecf20Sopenharmony_ci	}
29358c2ecf20Sopenharmony_ci
29368c2ecf20Sopenharmony_ci	cep = ep->dd_data;
29378c2ecf20Sopenharmony_ci	cep->csk = csk;
29388c2ecf20Sopenharmony_ci	cep->chba = hba;
29398c2ecf20Sopenharmony_ci
29408c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_SOCK,
29418c2ecf20Sopenharmony_ci		"ep 0x%p, cep 0x%p, csk 0x%p, hba 0x%p,%s.\n",
29428c2ecf20Sopenharmony_ci		ep, cep, csk, hba, hba->ndev->name);
29438c2ecf20Sopenharmony_ci	return ep;
29448c2ecf20Sopenharmony_ci
29458c2ecf20Sopenharmony_cirelease_conn:
29468c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
29478c2ecf20Sopenharmony_ci	cxgbi_sock_closed(csk);
29488c2ecf20Sopenharmony_cierr_out:
29498c2ecf20Sopenharmony_ci	return ERR_PTR(err);
29508c2ecf20Sopenharmony_ci}
29518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_ep_connect);
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ciint cxgbi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
29548c2ecf20Sopenharmony_ci{
29558c2ecf20Sopenharmony_ci	struct cxgbi_endpoint *cep = ep->dd_data;
29568c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = cep->csk;
29578c2ecf20Sopenharmony_ci
29588c2ecf20Sopenharmony_ci	if (!cxgbi_sock_is_established(csk))
29598c2ecf20Sopenharmony_ci		return 0;
29608c2ecf20Sopenharmony_ci	return 1;
29618c2ecf20Sopenharmony_ci}
29628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_ep_poll);
29638c2ecf20Sopenharmony_ci
29648c2ecf20Sopenharmony_civoid cxgbi_ep_disconnect(struct iscsi_endpoint *ep)
29658c2ecf20Sopenharmony_ci{
29668c2ecf20Sopenharmony_ci	struct cxgbi_endpoint *cep = ep->dd_data;
29678c2ecf20Sopenharmony_ci	struct cxgbi_conn *cconn = cep->cconn;
29688c2ecf20Sopenharmony_ci	struct cxgbi_sock *csk = cep->csk;
29698c2ecf20Sopenharmony_ci
29708c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_SOCK,
29718c2ecf20Sopenharmony_ci		"ep 0x%p, cep 0x%p, cconn 0x%p, csk 0x%p,%u,0x%lx.\n",
29728c2ecf20Sopenharmony_ci		ep, cep, cconn, csk, csk->state, csk->flags);
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci	if (cconn && cconn->iconn) {
29758c2ecf20Sopenharmony_ci		iscsi_suspend_tx(cconn->iconn);
29768c2ecf20Sopenharmony_ci		write_lock_bh(&csk->callback_lock);
29778c2ecf20Sopenharmony_ci		cep->csk->user_data = NULL;
29788c2ecf20Sopenharmony_ci		cconn->cep = NULL;
29798c2ecf20Sopenharmony_ci		write_unlock_bh(&csk->callback_lock);
29808c2ecf20Sopenharmony_ci	}
29818c2ecf20Sopenharmony_ci	iscsi_destroy_endpoint(ep);
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci	if (likely(csk->state >= CTP_ESTABLISHED))
29848c2ecf20Sopenharmony_ci		need_active_close(csk);
29858c2ecf20Sopenharmony_ci	else
29868c2ecf20Sopenharmony_ci		cxgbi_sock_closed(csk);
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ci	cxgbi_sock_put(csk);
29898c2ecf20Sopenharmony_ci}
29908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_ep_disconnect);
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ciint cxgbi_iscsi_init(struct iscsi_transport *itp,
29938c2ecf20Sopenharmony_ci			struct scsi_transport_template **stt)
29948c2ecf20Sopenharmony_ci{
29958c2ecf20Sopenharmony_ci	*stt = iscsi_register_transport(itp);
29968c2ecf20Sopenharmony_ci	if (*stt == NULL) {
29978c2ecf20Sopenharmony_ci		pr_err("unable to register %s transport 0x%p.\n",
29988c2ecf20Sopenharmony_ci			itp->name, itp);
29998c2ecf20Sopenharmony_ci		return -ENODEV;
30008c2ecf20Sopenharmony_ci	}
30018c2ecf20Sopenharmony_ci	log_debug(1 << CXGBI_DBG_ISCSI,
30028c2ecf20Sopenharmony_ci		"%s, registered iscsi transport 0x%p.\n",
30038c2ecf20Sopenharmony_ci		itp->name, stt);
30048c2ecf20Sopenharmony_ci	return 0;
30058c2ecf20Sopenharmony_ci}
30068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_iscsi_init);
30078c2ecf20Sopenharmony_ci
30088c2ecf20Sopenharmony_civoid cxgbi_iscsi_cleanup(struct iscsi_transport *itp,
30098c2ecf20Sopenharmony_ci			struct scsi_transport_template **stt)
30108c2ecf20Sopenharmony_ci{
30118c2ecf20Sopenharmony_ci	if (*stt) {
30128c2ecf20Sopenharmony_ci		log_debug(1 << CXGBI_DBG_ISCSI,
30138c2ecf20Sopenharmony_ci			"de-register transport 0x%p, %s, stt 0x%p.\n",
30148c2ecf20Sopenharmony_ci			itp, itp->name, *stt);
30158c2ecf20Sopenharmony_ci		*stt = NULL;
30168c2ecf20Sopenharmony_ci		iscsi_unregister_transport(itp);
30178c2ecf20Sopenharmony_ci	}
30188c2ecf20Sopenharmony_ci}
30198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_iscsi_cleanup);
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_ciumode_t cxgbi_attr_is_visible(int param_type, int param)
30228c2ecf20Sopenharmony_ci{
30238c2ecf20Sopenharmony_ci	switch (param_type) {
30248c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM:
30258c2ecf20Sopenharmony_ci		switch (param) {
30268c2ecf20Sopenharmony_ci		case ISCSI_HOST_PARAM_NETDEV_NAME:
30278c2ecf20Sopenharmony_ci		case ISCSI_HOST_PARAM_HWADDRESS:
30288c2ecf20Sopenharmony_ci		case ISCSI_HOST_PARAM_IPADDRESS:
30298c2ecf20Sopenharmony_ci		case ISCSI_HOST_PARAM_INITIATOR_NAME:
30308c2ecf20Sopenharmony_ci			return S_IRUGO;
30318c2ecf20Sopenharmony_ci		default:
30328c2ecf20Sopenharmony_ci			return 0;
30338c2ecf20Sopenharmony_ci		}
30348c2ecf20Sopenharmony_ci	case ISCSI_PARAM:
30358c2ecf20Sopenharmony_ci		switch (param) {
30368c2ecf20Sopenharmony_ci		case ISCSI_PARAM_MAX_RECV_DLENGTH:
30378c2ecf20Sopenharmony_ci		case ISCSI_PARAM_MAX_XMIT_DLENGTH:
30388c2ecf20Sopenharmony_ci		case ISCSI_PARAM_HDRDGST_EN:
30398c2ecf20Sopenharmony_ci		case ISCSI_PARAM_DATADGST_EN:
30408c2ecf20Sopenharmony_ci		case ISCSI_PARAM_CONN_ADDRESS:
30418c2ecf20Sopenharmony_ci		case ISCSI_PARAM_CONN_PORT:
30428c2ecf20Sopenharmony_ci		case ISCSI_PARAM_EXP_STATSN:
30438c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PERSISTENT_ADDRESS:
30448c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PERSISTENT_PORT:
30458c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PING_TMO:
30468c2ecf20Sopenharmony_ci		case ISCSI_PARAM_RECV_TMO:
30478c2ecf20Sopenharmony_ci		case ISCSI_PARAM_INITIAL_R2T_EN:
30488c2ecf20Sopenharmony_ci		case ISCSI_PARAM_MAX_R2T:
30498c2ecf20Sopenharmony_ci		case ISCSI_PARAM_IMM_DATA_EN:
30508c2ecf20Sopenharmony_ci		case ISCSI_PARAM_FIRST_BURST:
30518c2ecf20Sopenharmony_ci		case ISCSI_PARAM_MAX_BURST:
30528c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PDU_INORDER_EN:
30538c2ecf20Sopenharmony_ci		case ISCSI_PARAM_DATASEQ_INORDER_EN:
30548c2ecf20Sopenharmony_ci		case ISCSI_PARAM_ERL:
30558c2ecf20Sopenharmony_ci		case ISCSI_PARAM_TARGET_NAME:
30568c2ecf20Sopenharmony_ci		case ISCSI_PARAM_TPGT:
30578c2ecf20Sopenharmony_ci		case ISCSI_PARAM_USERNAME:
30588c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PASSWORD:
30598c2ecf20Sopenharmony_ci		case ISCSI_PARAM_USERNAME_IN:
30608c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PASSWORD_IN:
30618c2ecf20Sopenharmony_ci		case ISCSI_PARAM_FAST_ABORT:
30628c2ecf20Sopenharmony_ci		case ISCSI_PARAM_ABORT_TMO:
30638c2ecf20Sopenharmony_ci		case ISCSI_PARAM_LU_RESET_TMO:
30648c2ecf20Sopenharmony_ci		case ISCSI_PARAM_TGT_RESET_TMO:
30658c2ecf20Sopenharmony_ci		case ISCSI_PARAM_IFACE_NAME:
30668c2ecf20Sopenharmony_ci		case ISCSI_PARAM_INITIATOR_NAME:
30678c2ecf20Sopenharmony_ci			return S_IRUGO;
30688c2ecf20Sopenharmony_ci		default:
30698c2ecf20Sopenharmony_ci			return 0;
30708c2ecf20Sopenharmony_ci		}
30718c2ecf20Sopenharmony_ci	}
30728c2ecf20Sopenharmony_ci
30738c2ecf20Sopenharmony_ci	return 0;
30748c2ecf20Sopenharmony_ci}
30758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cxgbi_attr_is_visible);
30768c2ecf20Sopenharmony_ci
30778c2ecf20Sopenharmony_cistatic int __init libcxgbi_init_module(void)
30788c2ecf20Sopenharmony_ci{
30798c2ecf20Sopenharmony_ci	pr_info("%s", version);
30808c2ecf20Sopenharmony_ci
30818c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof_field(struct sk_buff, cb) <
30828c2ecf20Sopenharmony_ci		     sizeof(struct cxgbi_skb_cb));
30838c2ecf20Sopenharmony_ci	rsvd_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
30848c2ecf20Sopenharmony_ci	if (!rsvd_page)
30858c2ecf20Sopenharmony_ci		return -ENOMEM;
30868c2ecf20Sopenharmony_ci
30878c2ecf20Sopenharmony_ci	return 0;
30888c2ecf20Sopenharmony_ci}
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_cistatic void __exit libcxgbi_exit_module(void)
30918c2ecf20Sopenharmony_ci{
30928c2ecf20Sopenharmony_ci	cxgbi_device_unregister_all(0xFF);
30938c2ecf20Sopenharmony_ci	put_page(rsvd_page);
30948c2ecf20Sopenharmony_ci	return;
30958c2ecf20Sopenharmony_ci}
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_cimodule_init(libcxgbi_init_module);
30988c2ecf20Sopenharmony_cimodule_exit(libcxgbi_exit_module);
3099