From: Valentine Fatiev <valentinef@nvidia.com>
Subject: [PATCH] BACKPORT: drivers/infiniband/ulp/iser/iscsi_iser.c

Change-Id: I14c334184582c18ab629558c244fe4e27b605458
---
 drivers/infiniband/ulp/iser/iscsi_iser.c | 128 ++++++++++++++++++++++-
 1 file changed, 126 insertions(+), 2 deletions(-)

--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -76,6 +76,9 @@
 
 MODULE_DESCRIPTION("iSER (iSCSI Extensions for RDMA) Datamover");
 MODULE_LICENSE("Dual BSD/GPL");
+#ifdef RETPOLINE_MLNX
+MODULE_INFO(retpoline, "Y");
+#endif
 MODULE_AUTHOR("Alex Nezhinsky, Dan Bar Dov, Or Gerlitz");
 
 static struct scsi_host_template iscsi_iser_sht;
@@ -113,6 +116,12 @@ bool iser_pi_enable = false;
 module_param_named(pi_enable, iser_pi_enable, bool, S_IRUGO);
 MODULE_PARM_DESC(pi_enable, "Enable T10-PI offload support (default:disabled)");
 
+#ifndef HAVE_SCSI_CMND_PROT_FLAGS
+int iser_pi_guard;
+module_param_named(pi_guard, iser_pi_guard, int, S_IRUGO);
+MODULE_PARM_DESC(pi_guard, "T10-PI guard_type, 0:CRC|1:IP_CSUM (default:IP_CSUM)");
+#endif
+
 static int iscsi_iser_set(const char *val, const struct kernel_param *kp)
 {
 	int ret;
@@ -221,6 +230,27 @@ int iser_initialize_task_headers(struct
 	return 0;
 }
 
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
+/**
+ * set_last_ping_on_nopout_task()
+ * @task: iscsi task
+ *
+ * Workaround libiscsi not setting iscsi_conn->last_ping
+ * in case of failure.
+ * fixed in kernels > 4.3
+ */
+static inline void
+set_last_ping_on_nopout_task(struct iscsi_task *task)
+{
+	u8 task_opcode = (task->hdr->opcode & ISCSI_OPCODE_MASK);
+
+	if (task_opcode == ISCSI_OP_NOOP_OUT)
+		task->conn->last_ping = jiffies;
+}
+#endif
+
+
 /**
  * iscsi_iser_task_init() - Initialize iscsi-iser task
  * @task: iscsi task
@@ -237,8 +267,18 @@ static int iscsi_iser_task_init(struct i
 
 	ret = iser_initialize_task_headers(task, &iser_task->desc);
 	if (ret) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
+		u8 task_opcode = (task->hdr->opcode & ISCSI_OPCODE_MASK);
+
+		iser_err("Failed to init task %p, opcode %d, err = %d",
+			 iser_task, task_opcode, ret);
+
+		set_last_ping_on_nopout_task(task);
+#else
 		iser_err("Failed to init task %p, err = %d\n",
 			 iser_task, ret);
+#endif
+
 		return ret;
 	}
 
@@ -272,6 +312,17 @@ static int iscsi_iser_mtask_xmit(struct
 	iser_dbg("mtask xmit [cid %d itt 0x%x]\n", conn->id, task->itt);
 
 	error = iser_send_control(conn, task);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
+	if (error) {
+		u8 task_opcode = (task->hdr->opcode & ISCSI_OPCODE_MASK);
+
+		iser_err("Failed to send task %p, opcode %d, err = %d",
+			 task->dd_data, task_opcode, error);
+
+		set_last_ping_on_nopout_task(task);
+
+	}
+#endif
 
 	/* since iser xmits control with zero copy, tasks can not be recycled
 	 * right after sending them.
@@ -493,7 +544,9 @@ static int iscsi_iser_conn_bind(struct i
 	iser_conn->iscsi_conn = conn;
 
 out:
+#ifdef HAVE_ISCSI_PUT_ENDPOINT
 	iscsi_put_endpoint(ep);
+#endif
 	mutex_unlock(&iser_conn->state_mutex);
 	return error;
 }
@@ -568,7 +621,11 @@ static void iscsi_iser_session_destroy(s
 	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
 
 	iscsi_session_teardown(cls_session);
+#ifdef HAVE_ISCSI_HOST_REMOVE_2_PARAMS
+	iscsi_host_remove(shost, false);
+#else
 	iscsi_host_remove(shost);
+#endif
 	iscsi_host_free(shost);
 }
 
@@ -578,7 +635,9 @@ static inline unsigned int iser_dif_prot
 
 	if (prot_caps & IB_PROT_T10DIF_TYPE_1)
 		ret |= SHOST_DIF_TYPE1_PROTECTION |
+#ifdef HAVE_SCSI_CMND_PROT_FLAGS
 		       SHOST_DIX_TYPE0_PROTECTION |
+#endif
 		       SHOST_DIX_TYPE1_PROTECTION;
 	if (prot_caps & IB_PROT_T10DIF_TYPE_2)
 		ret |= SHOST_DIF_TYPE2_PROTECTION |
@@ -646,13 +705,20 @@ iscsi_iser_session_create(struct iscsi_e
 
 			shost->sg_prot_tablesize = shost->sg_tablesize;
 			scsi_host_set_prot(shost, iser_dif_prot_caps(sig_caps));
+#ifdef HAVE_SCSI_CMND_PROT_FLAGS
 			scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP |
 						   SHOST_DIX_GUARD_CRC);
+#else
+			if (iser_pi_guard)
+				scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP);
+			else
+				scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+#endif
 		}
-
+#ifdef HAVE_SCSI_HOST_VIRT_BOUNDARY_MASK
 		if (!(ib_dev->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG))
 			shost->virt_boundary_mask = SZ_4K - 1;
-
+#endif
 		if (iscsi_host_add(shost, ib_dev->dev.parent)) {
 			mutex_unlock(&iser_conn->state_mutex);
 			goto free_host;
@@ -685,7 +751,11 @@ iscsi_iser_session_create(struct iscsi_e
 	return cls_session;
 
 remove_host:
+#ifdef HAVE_ISCSI_HOST_REMOVE_2_PARAMS
+	iscsi_host_remove(shost, false);
+#else
 	iscsi_host_remove(shost);
+#endif
 free_host:
 	iscsi_host_free(shost);
 	return NULL;
@@ -756,7 +826,13 @@ static void iscsi_iser_conn_get_stats(st
 	stats->r2t_pdus = conn->r2t_pdus_cnt; /* always 0 */
 	stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
 	stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+#ifdef HAVE_VIRT_BOUNDARY
 	stats->custom_length = 0;
+#else
+	stats->custom_length = 1;
+	strcpy(stats->custom[0].desc, "fmr_unalign_cnt");
+	stats->custom[0].value = conn->fmr_unalign_cnt;
+#endif
 }
 
 static int iscsi_iser_get_ep_param(struct iscsi_endpoint *ep,
@@ -956,21 +1032,60 @@ static umode_t iser_attr_is_visible(int
 	return 0;
 }
 
+#ifdef USE_SLAVE_ALLOC_HANDLER
+static int iscsi_iser_slave_alloc(struct scsi_device *sdev)
+{
+	struct iscsi_session *session;
+	struct iser_conn *iser_conn;
+	struct ib_device *ib_dev;
+
+	mutex_lock(&unbind_iser_conn_mutex);
+
+	session = starget_to_session(scsi_target(sdev))->dd_data;
+	iser_conn = session->leadconn->dd_data;
+	if (!iser_conn) {
+		mutex_unlock(&unbind_iser_conn_mutex);
+		return -ENOTCONN;
+	}
+	ib_dev = iser_conn->ib_conn.device->ib_device;
+
+	if (!(ib_dev->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG))
+		blk_queue_virt_boundary(sdev->request_queue, ~(SZ_4K - 1));
+
+	mutex_unlock(&unbind_iser_conn_mutex);
+	return 0;
+}
+#endif
+
 static struct scsi_host_template iscsi_iser_sht = {
 	.module                 = THIS_MODULE,
 	.name                   = "iSCSI Initiator over iSER",
 	.queuecommand           = iscsi_queuecommand,
+#ifdef HAVE_SCSI_CHANGE_QUEUE_DEPTH
 	.change_queue_depth	= scsi_change_queue_depth,
+#else
+	.change_queue_depth	= iscsi_change_queue_depth,
+#endif
 	.sg_tablesize           = ISCSI_ISER_DEF_SG_TABLESIZE,
 	.cmd_per_lun            = ISER_DEF_CMD_PER_LUN,
+#ifdef HAVE_ISCSI_EH_CMD_TIMED_OUT
 	.eh_timed_out		= iscsi_eh_cmd_timed_out,
+#endif
 	.eh_abort_handler       = iscsi_eh_abort,
 	.eh_device_reset_handler= iscsi_eh_device_reset,
 	.eh_target_reset_handler = iscsi_eh_recover_target,
 	.target_alloc		= iscsi_target_alloc,
+#ifdef ENABLE_CLUSTERING
+	.use_clustering		= ENABLE_CLUSTERING,
+#endif
+#ifdef USE_SLAVE_ALLOC_HANDLER
+	.slave_alloc		= iscsi_iser_slave_alloc,
+#endif
 	.proc_name              = "iscsi_iser",
 	.this_id                = -1,
+#ifdef HAVE_SCSI_HOST_TEMPLATE_TRACK_QUEUE_DEPTH
 	.track_queue_depth	= 1,
+#endif
 };
 
 static struct iscsi_transport iscsi_iser_transport = {
@@ -983,7 +1098,9 @@ static struct iscsi_transport iscsi_iser
 	/* connection management */
 	.create_conn            = iscsi_iser_conn_create,
 	.bind_conn              = iscsi_iser_conn_bind,
+#ifdef HAVE_ISCSI_CONN_UNBIND
 	.unbind_conn		= iscsi_conn_unbind,
+#endif
 	.destroy_conn           = iscsi_conn_teardown,
 	.attr_is_visible	= iser_attr_is_visible,
 	.set_param              = iscsi_iser_set_param,
@@ -1017,6 +1134,13 @@ static int __init iser_init(void)
 
 	iser_dbg("Starting iSER datamover...\n");
 
+#ifndef HAVE_SCSI_CMND_PROT_FLAGS
+	if (iser_pi_guard < 0 || iser_pi_guard > 1) {
+		iser_err("Invalid pi_guard value of %d\n", iser_pi_guard);
+		return -EINVAL;
+	}
+#endif
+
 	memset(&ig, 0, sizeof(struct iser_global));
 
 	ig.desc_cache = kmem_cache_create("iser_descriptors",
